From 567a44900370d869717c2be65bc0f71402ee1f6b Mon Sep 17 00:00:00 2001 From: "Rafael H. Schloming" Date: Tue, 6 Jan 2009 18:51:41 +0000 Subject: Tag M4-RC6 git-svn-id: https://svn.apache.org/repos/asf/qpid/tags/M4@732053 13f79535-47bb-0310-9956-ffa450edef68 --- RC6/qpid/java/010ExcludeList | 57 + RC6/qpid/java/010ExcludeList-noPrefetch | 61 + RC6/qpid/java/010ExcludeList-store | 64 + RC6/qpid/java/08ExcludeList | 8 + RC6/qpid/java/08ExcludeList-nonvm | 29 + RC6/qpid/java/ExcludeList | 8 + RC6/qpid/java/KEYS | 0 RC6/qpid/java/XAExcludeList | 3 + RC6/qpid/java/broker-plugins/MANIFEST.MF | 14 + RC6/qpid/java/broker-plugins/build.xml | 29 + .../java/org/apache/qpid/extras/Activator.java | 48 + .../exchanges/diagnostic/DiagnosticExchange.java | 219 ++ .../diagnostic/DiagnosticExchangeType.java | 57 + .../extras/exchanges/example/TestExchange.java | 118 + .../extras/exchanges/example/TestExchangeType.java | 57 + RC6/qpid/java/broker/bin/msTool.sh | 60 + RC6/qpid/java/broker/bin/qpid-passwd | 35 + RC6/qpid/java/broker/bin/qpid-server | 37 + RC6/qpid/java/broker/bin/qpid-server-bdb.bat | 22 + RC6/qpid/java/broker/bin/qpid-server.bat | 203 ++ RC6/qpid/java/broker/bin/qpid.start | 21 + RC6/qpid/java/broker/bin/qpid.stop | 178 ++ RC6/qpid/java/broker/bin/qpid.stopall | 27 + RC6/qpid/java/broker/build.xml | 57 + RC6/qpid/java/broker/etc/access | 19 + RC6/qpid/java/broker/etc/acl.config.xml | 231 ++ RC6/qpid/java/broker/etc/config.xml | 133 + RC6/qpid/java/broker/etc/debug.log4j.xml | 114 + RC6/qpid/java/broker/etc/jmxremote.access | 23 + RC6/qpid/java/broker/etc/log4j.xml | 98 + RC6/qpid/java/broker/etc/md5passwd | 21 + RC6/qpid/java/broker/etc/mstool-log4j.xml | 54 + RC6/qpid/java/broker/etc/passwd | 22 + RC6/qpid/java/broker/etc/passwdVhost | 19 + RC6/qpid/java/broker/etc/persistent_config.xml | 115 + RC6/qpid/java/broker/etc/qpid-server.conf | 25 + RC6/qpid/java/broker/etc/qpid-server.conf.jpp | 49 + RC6/qpid/java/broker/etc/qpid.passwd | 23 + RC6/qpid/java/broker/etc/transient_config.xml | 112 + RC6/qpid/java/broker/etc/virtualhosts.xml | 123 + RC6/qpid/java/broker/python-test.xml | 56 + .../java/broker/src/main/grammar/SelectorParser.jj | 621 +++++ .../java/broker/src/main/java/log4j.properties | 24 + .../apache/log4j/QpidCompositeRollingAppender.java | 1007 +++++++ .../apache/qpid/configuration/Configuration.java | 188 ++ .../apache/qpid/server/AMQBrokerManagerMBean.java | 239 ++ .../java/org/apache/qpid/server/AMQChannel.java | 913 ++++++ .../qpid/server/ConsumerTagNotUniqueException.java | 25 + .../qpid/server/ExtractResendAndRequeue.java | 108 + .../src/main/java/org/apache/qpid/server/Main.java | 554 ++++ .../org/apache/qpid/server/ManagedChannel.java | 68 + .../qpid/server/RequiredDeliveryException.java | 79 + .../java/org/apache/qpid/server/ack/TxAck.java | 148 + .../qpid/server/ack/UnacknowledgedMessageMap.java | 81 + .../server/ack/UnacknowledgedMessageMapImpl.java | 232 ++ .../qpid/server/configuration/Configurator.java | 118 + .../configuration/VirtualHostConfiguration.java | 286 ++ .../qpid/server/connection/ConnectionRegistry.java | 73 + .../server/connection/IConnectionRegistry.java | 38 + .../qpid/server/exchange/AbstractExchange.java | 215 ++ .../server/exchange/DefaultExchangeFactory.java | 113 + .../server/exchange/DefaultExchangeRegistry.java | 139 + .../qpid/server/exchange/DirectExchange.java | 253 ++ .../org/apache/qpid/server/exchange/Exchange.java | 98 + .../qpid/server/exchange/ExchangeFactory.java | 40 + .../server/exchange/ExchangeInUseException.java | 45 + .../qpid/server/exchange/ExchangeRegistry.java | 51 + .../apache/qpid/server/exchange/ExchangeType.java | 35 + .../qpid/server/exchange/FanoutExchange.java | 224 ++ .../qpid/server/exchange/HeadersBinding.java | 219 ++ .../qpid/server/exchange/HeadersExchange.java | 350 +++ .../org/apache/qpid/server/exchange/Index.java | 99 + .../qpid/server/exchange/ManagedExchange.java | 98 + .../apache/qpid/server/exchange/MessageRouter.java | 41 + .../qpid/server/exchange/NoRouteException.java | 49 + .../apache/qpid/server/exchange/TopicExchange.java | 670 +++++ .../qpid/server/exchange/headers/HeaderKey.java | 40 + .../exchange/headers/HeaderKeyDictionary.java | 50 + .../exchange/headers/HeaderMatcherResult.java | 25 + .../exchange/headers/HeadersMatcherDFAState.java | 339 +++ .../server/exchange/headers/HeadersParser.java | 439 +++ .../exchange/topic/TopicMatcherDFAState.java | 295 ++ .../server/exchange/topic/TopicMatcherResult.java | 25 + .../qpid/server/exchange/topic/TopicParser.java | 613 +++++ .../qpid/server/exchange/topic/TopicWord.java | 54 + .../server/exchange/topic/TopicWordDictionary.java | 63 + .../qpid/server/filter/ArithmeticExpression.java | 275 ++ .../qpid/server/filter/BinaryExpression.java | 106 + .../qpid/server/filter/BooleanExpression.java | 41 + .../qpid/server/filter/ComparisonExpression.java | 601 ++++ .../qpid/server/filter/ConstantExpression.java | 211 ++ .../org/apache/qpid/server/filter/Expression.java | 38 + .../apache/qpid/server/filter/FilterManager.java | 39 + .../qpid/server/filter/FilterManagerFactory.java | 67 + .../qpid/server/filter/JMSSelectorFilter.java | 56 + .../apache/qpid/server/filter/LogicExpression.java | 122 + .../apache/qpid/server/filter/MessageFilter.java | 30 + .../qpid/server/filter/NoConsumerFilter.java | 42 + .../qpid/server/filter/PropertyExpression.java | 268 ++ .../qpid/server/filter/SimpleFilterManager.java | 77 + .../apache/qpid/server/filter/UnaryExpression.java | 369 +++ .../apache/qpid/server/filter/XPathExpression.java | 127 + .../qpid/server/filter/XQueryExpression.java | 58 + .../qpid/server/filter/XalanXPathEvaluator.java | 103 + .../server/flow/AbstractFlowCreditManager.java | 62 + .../qpid/server/flow/BytesOnlyCreditManager.java | 77 + .../apache/qpid/server/flow/FlowCreditManager.java | 44 + .../qpid/server/flow/LimitlessCreditManager.java | 44 + .../server/flow/MessageAndBytesCreditManager.java | 79 + .../qpid/server/flow/MessageOnlyCreditManager.java | 76 + .../qpid/server/flow/Pre0_10CreditManager.java | 185 ++ .../qpid/server/handler/AccessRequestHandler.java | 61 + .../qpid/server/handler/BasicAckMethodHandler.java | 67 + .../server/handler/BasicCancelMethodHandler.java | 74 + .../server/handler/BasicConsumeMethodHandler.java | 168 ++ .../qpid/server/handler/BasicGetMethodHandler.java | 187 ++ .../server/handler/BasicPublishMethodHandler.java | 101 + .../qpid/server/handler/BasicQosHandler.java | 58 + .../server/handler/BasicRecoverMethodHandler.java | 73 + .../handler/BasicRecoverSyncMethodHandler.java | 75 + .../server/handler/BasicRejectMethodHandler.java | 125 + .../qpid/server/handler/ChannelCloseHandler.java | 77 + .../qpid/server/handler/ChannelCloseOkHandler.java | 53 + .../qpid/server/handler/ChannelFlowHandler.java | 66 + .../qpid/server/handler/ChannelOpenHandler.java | 103 + .../handler/ConnectionCloseMethodHandler.java | 72 + .../handler/ConnectionCloseOkMethodHandler.java | 63 + .../handler/ConnectionOpenMethodHandler.java | 99 + .../handler/ConnectionSecureOkMethodHandler.java | 124 + .../handler/ConnectionStartOkMethodHandler.java | 164 ++ .../handler/ConnectionTuneOkMethodHandler.java | 54 + .../qpid/server/handler/ExchangeBoundHandler.java | 178 ++ .../server/handler/ExchangeDeclareHandler.java | 116 + .../qpid/server/handler/ExchangeDeleteHandler.java | 71 + .../server/handler/OnCurrentThreadExecutor.java | 34 + .../qpid/server/handler/QueueBindHandler.java | 139 + .../qpid/server/handler/QueueDeclareHandler.java | 209 ++ .../qpid/server/handler/QueueDeleteHandler.java | 124 + .../qpid/server/handler/QueuePurgeHandler.java | 119 + .../qpid/server/handler/QueueUnbindHandler.java | 134 + .../server/handler/ServerMethodDispatcherImpl.java | 566 ++++ .../handler/ServerMethodDispatcherImpl_0_9.java | 164 ++ .../handler/ServerMethodDispatcherImpl_8_0.java | 86 + .../qpid/server/handler/TxCommitHandler.java | 78 + .../qpid/server/handler/TxRollbackHandler.java | 77 + .../qpid/server/handler/TxSelectHandler.java | 63 + .../server/handler/UnexpectedMethodException.java | 33 + .../org/apache/qpid/server/jms/JmsConsumer.java | 110 + .../qpid/server/management/AMQManagedObject.java | 97 + .../server/management/DefaultManagedObject.java | 191 ++ .../management/JMXManagedObjectRegistry.java | 224 ++ .../qpid/server/management/MBeanAttribute.java | 41 + .../qpid/server/management/MBeanConstructor.java | 39 + .../qpid/server/management/MBeanDescription.java | 38 + .../qpid/server/management/MBeanIntrospector.java | 388 +++ .../management/MBeanInvocationHandlerImpl.java | 239 ++ .../qpid/server/management/MBeanOperation.java | 43 + .../server/management/MBeanOperationParameter.java | 37 + .../apache/qpid/server/management/Managable.java | 34 + .../qpid/server/management/ManagedBroker.java | 98 + .../qpid/server/management/ManagedObject.java | 58 + .../server/management/ManagedObjectRegistry.java | 48 + .../server/management/ManagementConfiguration.java | 30 + .../management/NoopManagedObjectRegistry.java | 60 + .../server/output/ProtocolOutputConverter.java | 57 + .../output/ProtocolOutputConverterRegistry.java | 61 + .../amqp0_8/ProtocolOutputConverterImpl.java | 284 ++ .../amqp0_9/ProtocolOutputConverterImpl.java | 391 +++ .../org/apache/qpid/server/plugins/Activator.java | 44 + .../apache/qpid/server/plugins/PluginManager.java | 145 + .../server/protocol/AMQMinaProtocolSession.java | 859 ++++++ .../protocol/AMQNoMethodHandlerException.java | 46 + .../server/protocol/AMQPFastProtocolHandler.java | 280 ++ .../qpid/server/protocol/AMQPProtocolProvider.java | 52 + .../qpid/server/protocol/AMQProtocolSession.java | 207 ++ .../server/protocol/AMQProtocolSessionMBean.java | 306 ++ .../qpid/server/protocol/ExchangeInitialiser.java | 51 + .../qpid/server/protocol/HeartbeatConfig.java | 67 + .../qpid/server/protocol/ManagedConnection.java | 135 + .../protocol/UnknnownMessageTypeException.java | 46 + .../org/apache/qpid/server/queue/AMQMessage.java | 482 ++++ .../apache/qpid/server/queue/AMQMessageHandle.java | 79 + .../apache/qpid/server/queue/AMQPriorityQueue.java | 71 + .../org/apache/qpid/server/queue/AMQQueue.java | 216 ++ .../apache/qpid/server/queue/AMQQueueFactory.java | 78 + .../apache/qpid/server/queue/AMQQueueMBean.java | 479 ++++ .../qpid/server/queue/AsyncDeliveryConfig.java | 56 + .../qpid/server/queue/DefaultQueueRegistry.java | 71 + .../apache/qpid/server/queue/ExchangeBinding.java | 84 + .../apache/qpid/server/queue/ExchangeBindings.java | 82 + .../qpid/server/queue/FailedDequeueException.java | 50 + .../org/apache/qpid/server/queue/Filterable.java | 33 + .../qpid/server/queue/InMemoryMessageHandle.java | 157 ++ .../apache/qpid/server/queue/IncomingMessage.java | 319 +++ .../org/apache/qpid/server/queue/ManagedQueue.java | 245 ++ .../qpid/server/queue/MessageCleanupException.java | 52 + .../qpid/server/queue/MessageHandleFactory.java | 46 + .../apache/qpid/server/queue/MessageMetaData.java | 92 + .../qpid/server/queue/NoConsumersException.java | 47 + .../qpid/server/queue/NotificationCheck.java | 138 + .../qpid/server/queue/PriorityQueueList.java | 169 ++ .../org/apache/qpid/server/queue/QueueEntry.java | 184 ++ .../apache/qpid/server/queue/QueueEntryImpl.java | 388 +++ .../qpid/server/queue/QueueEntryIterator.java | 30 + .../apache/qpid/server/queue/QueueEntryList.java | 34 + .../qpid/server/queue/QueueEntryListFactory.java | 26 + .../server/queue/QueueNotificationListener.java | 27 + .../apache/qpid/server/queue/QueueRegistry.java | 43 + .../apache/qpid/server/queue/SimpleAMQQueue.java | 1604 +++++++++++ .../qpid/server/queue/SimpleQueueEntryList.java | 178 ++ .../qpid/server/queue/TransientMessageData.java | 127 + .../server/queue/UnauthorizedAccessException.java | 45 + .../server/queue/WeakReferenceMessageHandle.java | 219 ++ .../qpid/server/registry/ApplicationRegistry.java | 313 +++ .../ConfigurationFileApplicationRegistry.java | 140 + .../qpid/server/registry/IApplicationRegistry.java | 88 + .../qpid/server/security/access/ACLManager.java | 161 ++ .../qpid/server/security/access/ACLPlugin.java | 58 + .../qpid/server/security/access/AccessResult.java | 65 + .../qpid/server/security/access/AccessRights.java | 63 + .../qpid/server/security/access/Accessable.java | 27 + .../qpid/server/security/access/Permission.java | 37 + .../security/access/PrincipalPermissions.java | 579 ++++ .../server/security/access/VirtualHostAccess.java | 68 + .../access/management/AMQUserManagementMBean.java | 473 ++++ .../security/access/management/UserManagement.java | 118 + .../server/security/access/plugins/AllowAll.java | 68 + .../server/security/access/plugins/DenyAll.java | 57 + .../server/security/access/plugins/SimpleXML.java | 342 +++ .../server/security/auth/AuthenticationResult.java | 63 + .../Base64MD5PasswordFilePrincipalDatabase.java | 499 ++++ .../ConfigurationFilePrincipalDatabaseManager.java | 235 ++ .../server/security/auth/database/HashedUser.java | 134 + .../PlainPasswordFilePrincipalDatabase.java | 240 ++ .../security/auth/database/PrincipalDatabase.java | 100 + .../auth/database/PrincipalDatabaseManager.java | 34 + .../auth/database/PropertiesPrincipalDatabase.java | 164 ++ .../PropertiesPrincipalDatabaseManager.java | 48 + .../auth/manager/AuthenticationManager.java | 38 + .../PrincipalDatabaseAuthenticationManager.java | 246 ++ .../sasl/AuthenticationProviderInitialiser.java | 76 + .../server/security/auth/sasl/JCAProvider.java | 46 + .../auth/sasl/UsernamePasswordInitialiser.java | 123 + .../security/auth/sasl/UsernamePrincipal.java | 44 + .../auth/sasl/amqplain/AmqPlainInitialiser.java | 38 + .../auth/sasl/amqplain/AmqPlainSaslServer.java | 132 + .../sasl/amqplain/AmqPlainSaslServerFactory.java | 60 + .../sasl/crammd5/CRAMMD5HashedInitialiser.java | 50 + .../auth/sasl/crammd5/CRAMMD5HashedSaslServer.java | 105 + .../sasl/crammd5/CRAMMD5HashedServerFactory.java | 61 + .../auth/sasl/crammd5/CRAMMD5Initialiser.java | 71 + .../security/auth/sasl/plain/PlainInitialiser.java | 38 + .../security/auth/sasl/plain/PlainSaslServer.java | 151 + .../auth/sasl/plain/PlainSaslServerFactory.java | 60 + .../org/apache/qpid/server/state/AMQState.java | 36 + .../apache/qpid/server/state/AMQStateManager.java | 263 ++ .../state/IllegalStateTransitionException.java | 52 + .../server/state/StateAwareMethodListener.java | 35 + .../apache/qpid/server/state/StateListener.java | 30 + .../qpid/server/store/DerbyMessageStore.java | 1463 ++++++++++ .../qpid/server/store/MemoryMessageStore.java | 234 ++ .../org/apache/qpid/server/store/MessageStore.java | 276 ++ .../server/store/MessageStoreClosedException.java | 36 + .../org/apache/qpid/server/store/StoreContext.java | 71 + .../server/subscription/ClientDeliveryMethod.java | 29 + .../server/subscription/RecordDeliveryMethod.java | 28 + .../qpid/server/subscription/Subscription.java | 96 + .../server/subscription/SubscriptionFactory.java | 59 + .../subscription/SubscriptionFactoryImpl.java | 103 + .../qpid/server/subscription/SubscriptionImpl.java | 617 +++++ .../qpid/server/subscription/SubscriptionList.java | 247 ++ .../server/transport/ConnectorConfiguration.java | 118 + .../qpid/server/transport/ThreadPoolFilter.java | 705 +++++ .../qpid/server/txn/LocalTransactionalContext.java | 293 ++ .../qpid/server/txn/NonTransactionalContext.java | 213 ++ .../qpid/server/txn/StoreMessageOperation.java | 58 + .../qpid/server/txn/TransactionalContext.java | 179 ++ .../java/org/apache/qpid/server/txn/TxnBuffer.java | 109 + .../java/org/apache/qpid/server/txn/TxnOp.java | 55 + .../apache/qpid/server/util/CircularBuffer.java | 131 + .../server/util/ConcurrentLinkedQueueNoSize.java | 38 + .../org/apache/qpid/server/util/LoggingProxy.java | 105 + .../qpid/server/util/NullApplicationRegistry.java | 84 + .../server/virtualhost/ManagedVirtualHost.java | 44 + .../qpid/server/virtualhost/VirtualHost.java | 339 +++ .../server/virtualhost/VirtualHostRegistry.java | 70 + .../qpid/tools/messagestore/MessageStoreTool.java | 652 +++++ .../messagestore/commands/AbstractCommand.java | 66 + .../qpid/tools/messagestore/commands/Clear.java | 85 + .../qpid/tools/messagestore/commands/Command.java | 36 + .../qpid/tools/messagestore/commands/Copy.java | 55 + .../qpid/tools/messagestore/commands/Dump.java | 302 ++ .../qpid/tools/messagestore/commands/Help.java | 98 + .../qpid/tools/messagestore/commands/List.java | 314 +++ .../qpid/tools/messagestore/commands/Load.java | 94 + .../qpid/tools/messagestore/commands/Move.java | 206 ++ .../qpid/tools/messagestore/commands/Purge.java | 67 + .../qpid/tools/messagestore/commands/Quit.java | 54 + .../qpid/tools/messagestore/commands/Select.java | 233 ++ .../qpid/tools/messagestore/commands/Show.java | 515 ++++ .../org/apache/qpid/tools/security/Passwd.java | 81 + .../org/apache/qpid/tools/utils/CommandParser.java | 51 + .../java/org/apache/qpid/tools/utils/Console.java | 90 + .../qpid/tools/utils/SimpleCommandParser.java | 121 + .../org/apache/qpid/tools/utils/SimpleConsole.java | 363 +++ .../qpid/server/ExtractResendAndRequeueTest.java | 255 ++ .../apache/qpid/server/RunBrokerWithCommand.java | 132 + .../org/apache/qpid/server/SelectorParserTest.java | 128 + .../apache/qpid/server/ack/AcknowledgeTest.java | 120 + .../server/configuration/TestPropertyUtils.java | 50 + .../VirtualHostConfigurationTest.java | 108 + .../qpid/server/exchange/DestWildExchangeTest.java | 595 ++++ .../qpid/server/exchange/ExchangeMBeanTest.java | 145 + .../qpid/server/exchange/HeadersBindingTest.java | 199 ++ .../protocol/InternalTestProtocolSession.java | 180 ++ .../apache/qpid/server/protocol/TestIoSession.java | 295 ++ .../qpid/server/queue/AMQPriorityQueueTest.java | 107 + .../qpid/server/queue/AMQQueueAlertTest.java | 377 +++ .../qpid/server/queue/AMQQueueFactoryTest.java | 85 + .../qpid/server/queue/AMQQueueMBeanTest.java | 349 +++ .../apache/qpid/server/queue/MockAMQMessage.java | 49 + .../qpid/server/queue/MockAMQMessageHandle.java | 37 + .../org/apache/qpid/server/queue/MockAMQQueue.java | 313 +++ .../qpid/server/queue/MockMessagePublishInfo.java | 52 + .../apache/qpid/server/queue/MockQueueEntry.java | 197 ++ .../qpid/server/queue/SimpleAMQQueueTest.java | 432 +++ .../server/queue/SimpleAMQQueueThreadPoolTest.java | 59 + .../registry/ApplicationRegistryShutdownTest.java | 120 + .../management/AMQUserManagementMBeanTest.java | 152 + ...Base64MD5PasswordFilePrincipalDatabaseTest.java | 337 +++ .../security/auth/database/HashedUserTest.java | 95 + .../security/auth/sasl/SaslServerTestCase.java | 66 + .../security/auth/sasl/TestPrincipalDatabase.java | 86 + .../auth/sasl/amqplain/AMQPlainSaslServerTest.java | 43 + .../auth/sasl/plain/PlainSaslServerTest.java | 39 + .../server/store/MessageStoreShutdownTest.java | 81 + .../apache/qpid/server/store/MessageStoreTest.java | 644 +++++ .../server/store/TestableMemoryMessageStore.java | 92 + .../qpid/server/subscription/MockSubscription.java | 180 ++ .../subscription/QueueBrowserUsesNoAckTest.java | 77 + .../qpid/server/util/InternalBrokerBaseCase.java | 209 ++ .../apache/qpid/server/util/LoggingProxyTest.java | 88 + .../qpid/server/util/TestApplicationRegistry.java | 123 + .../java/org/apache/qpid/util/MockChannel.java | 43 + RC6/qpid/java/build.deps | 104 + RC6/qpid/java/build.xml | 223 ++ RC6/qpid/java/clean-dir | 25 + RC6/qpid/java/client-java14/README.txt | 33 + RC6/qpid/java/client-java14/etc/sasl.properties | 20 + .../src/main/assembly/client-java14-bin.xml | 74 + .../src/main/assembly/jar-with-dependencies.xml | 104 + .../org/apache/qpid/sasl/ClientFactoryImpl.java | 343 +++ .../java/org/apache/qpid/sasl/CramMD5Client.java | 347 +++ .../java/org/apache/qpid/sasl/PlainClient.java | 275 ++ .../main/java/org/apache/qpid/sasl/Provider.java | 61 + .../test/integration/client/ConnectionTest.java | 66 + RC6/qpid/java/client/build.xml | 48 + RC6/qpid/java/client/example/bin/README.txt | 11 + RC6/qpid/java/client/example/bin/set_classpath.bat | 50 + RC6/qpid/java/client/example/bin/set_classpath.sh | 83 + RC6/qpid/java/client/example/bin/verify_all | 59 + RC6/qpid/java/client/example/build.xml | 27 + RC6/qpid/java/client/example/source-jar.xml | 35 + .../java/client/example/src/main/java/README.txt | 17 + .../java/client/example/src/main/java/log4j.xml | 49 + .../example/amqpexample/direct/DeclareQueue.java | 55 + .../example/amqpexample/direct/DirectProducer.java | 67 + .../qpid/example/amqpexample/direct/Listener.java | 103 + .../example/amqpexample/fanout/DeclareQueue.java | 55 + .../amqpexample/fanout/FannoutProducer.java | 66 + .../qpid/example/amqpexample/fanout/Listener.java | 103 + .../example/amqpexample/pubsub/TopicListener.java | 112 + .../example/amqpexample/pubsub/TopicPublisher.java | 80 + .../qpid/example/jmsexample/direct/Consumer.java | 152 + .../qpid/example/jmsexample/direct/Listener.java | 208 ++ .../qpid/example/jmsexample/direct/Producer.java | 138 + .../example/jmsexample/direct/direct.properties | 27 + .../apache/qpid/example/jmsexample/direct/verify | 34 + .../qpid/example/jmsexample/direct/verify.in | 35 + .../qpid/example/jmsexample/direct/verify_cpp_java | 10 + .../example/jmsexample/direct/verify_cpp_java.in | 20 + .../qpid/example/jmsexample/direct/verify_java_cpp | 10 + .../example/jmsexample/direct/verify_java_cpp.in | 30 + .../example/jmsexample/direct/verify_java_python | 9 + .../jmsexample/direct/verify_java_python.in | 29 + .../example/jmsexample/direct/verify_python_java | 10 + .../jmsexample/direct/verify_python_java.in | 20 + .../qpid/example/jmsexample/fanout/Consumer.java | 165 ++ .../qpid/example/jmsexample/fanout/Listener.java | 201 ++ .../qpid/example/jmsexample/fanout/Producer.java | 113 + .../example/jmsexample/fanout/fanout.properties | 33 + .../apache/qpid/example/jmsexample/fanout/verify | 36 + .../qpid/example/jmsexample/fanout/verify.in | 70 + .../qpid/example/jmsexample/fanout/verify_cpp_java | 13 + .../example/jmsexample/fanout/verify_cpp_java.in | 55 + .../qpid/example/jmsexample/fanout/verify_java_cpp | 13 + .../example/jmsexample/fanout/verify_java_cpp.in | 58 + .../example/jmsexample/fanout/verify_java_python | 13 + .../jmsexample/fanout/verify_java_python.in | 55 + .../example/jmsexample/fanout/verify_python_java | 13 + .../jmsexample/fanout/verify_python_java.in | 55 + .../qpid/example/jmsexample/pubsub/Listener.java | 214 ++ .../qpid/example/jmsexample/pubsub/Publisher.java | 133 + .../example/jmsexample/pubsub/pubsub.properties | 36 + .../apache/qpid/example/jmsexample/pubsub/verify | 33 + .../qpid/example/jmsexample/pubsub/verify.in | 95 + .../qpid/example/jmsexample/pubsub/verify_cpp_java | 10 + .../example/jmsexample/pubsub/verify_cpp_java.in | 55 + .../qpid/example/jmsexample/pubsub/verify_java_cpp | 10 + .../example/jmsexample/pubsub/verify_java_cpp.in | 99 + .../example/jmsexample/pubsub/verify_java_python | 10 + .../jmsexample/pubsub/verify_java_python.in | 95 + .../example/jmsexample/pubsub/verify_python_java | 10 + .../jmsexample/pubsub/verify_python_java.in | 55 + .../example/jmsexample/requestResponse/Client.java | 153 + .../example/jmsexample/requestResponse/Server.java | 163 ++ .../requestResponse/requestResponse.properties | 27 + .../qpid/example/jmsexample/requestResponse/verify | 34 + .../example/jmsexample/requestResponse/verify.in | 38 + .../jmsexample/requestResponse/verify_cpp_java | 12 + .../jmsexample/requestResponse/verify_cpp_java.in | 22 + .../jmsexample/requestResponse/verify_java_cpp | 12 + .../jmsexample/requestResponse/verify_java_cpp.in | 35 + .../jmsexample/requestResponse/verify_java_python | 11 + .../requestResponse/verify_java_python.in | 34 + .../jmsexample/requestResponse/verify_python_java | 11 + .../requestResponse/verify_python_java.in | 18 + .../jmsexample/transacted/QueueToTopic.java | 259 ++ .../jmsexample/transacted/transacted.properties | 31 + .../example/publisher/FileMessageDispatcher.java | 163 ++ .../qpid/example/publisher/FileMessageFactory.java | 138 + .../example/publisher/MessageFactoryException.java | 29 + .../publisher/MonitorMessageDispatcher.java | 141 + .../qpid/example/publisher/MonitorPublisher.java | 105 + .../apache/qpid/example/publisher/Publisher.java | 181 ++ .../publisher/UndeliveredMessageException.java | 32 + .../org/apache/qpid/example/pubsub/Client.java | 72 + .../qpid/example/pubsub/ConnectionSetup.java | 123 + .../org/apache/qpid/example/pubsub/Publisher.java | 81 + .../org/apache/qpid/example/pubsub/Subscriber.java | 98 + .../qpid/example/shared/ConnectionException.java | 29 + .../qpid/example/shared/ContextException.java | 29 + .../org/apache/qpid/example/shared/FileUtils.java | 168 ++ .../qpid/example/shared/InitialContextHelper.java | 81 + .../org/apache/qpid/example/shared/Statics.java | 57 + .../apache/qpid/example/shared/example.properties | 39 + .../apache/qpid/example/simple/reqresp/Client.java | 263 ++ .../apache/qpid/example/simple/reqresp/Server.java | 236 ++ .../example/subscriber/MonitoredSubscriber.java | 139 + .../subscriber/MonitoredSubscriptionWrapper.java | 47 + .../apache/qpid/example/subscriber/Subscriber.java | 182 ++ .../example/subscriber/SubscriptionWrapper.java | 46 + .../transport/ExistingSocketConnectorDemo.java | 171 ++ .../java/client/example/src/main/java/runSample.sh | 72 + .../java/client/src/main/grammar/SelectorParser.jj | 609 ++++ RC6/qpid/java/client/src/main/java/client.bnd | 6 + RC6/qpid/java/client/src/main/java/client.log4j | 33 + .../socket/nio/ExistingSocketConnector.java | 478 ++++ .../qpid/client/AMQAuthenticationException.java | 47 + .../org/apache/qpid/client/AMQBrokerDetails.java | 390 +++ .../java/org/apache/qpid/client/AMQConnection.java | 1454 ++++++++++ .../apache/qpid/client/AMQConnectionDelegate.java | 50 + .../qpid/client/AMQConnectionDelegate_0_10.java | 269 ++ .../qpid/client/AMQConnectionDelegate_0_9.java | 32 + .../qpid/client/AMQConnectionDelegate_8_0.java | 287 ++ .../apache/qpid/client/AMQConnectionFactory.java | 552 ++++ .../org/apache/qpid/client/AMQConnectionURL.java | 294 ++ .../org/apache/qpid/client/AMQDestination.java | 507 ++++ .../org/apache/qpid/client/AMQHeadersExchange.java | 54 + .../qpid/client/AMQNoConsumersException.java | 40 + .../apache/qpid/client/AMQNoRouteException.java | 40 + .../main/java/org/apache/qpid/client/AMQQueue.java | 165 ++ .../org/apache/qpid/client/AMQQueueBrowser.java | 137 + .../apache/qpid/client/AMQQueueSessionAdaptor.java | 204 ++ .../java/org/apache/qpid/client/AMQSession.java | 2908 ++++++++++++++++++++ .../org/apache/qpid/client/AMQSessionAdapter.java | 26 + .../qpid/client/AMQSessionDirtyException.java | 42 + .../org/apache/qpid/client/AMQSession_0_10.java | 850 ++++++ .../org/apache/qpid/client/AMQSession_0_8.java | 565 ++++ .../org/apache/qpid/client/AMQTemporaryQueue.java | 69 + .../org/apache/qpid/client/AMQTemporaryTopic.java | 72 + .../main/java/org/apache/qpid/client/AMQTopic.java | 151 + .../apache/qpid/client/AMQTopicSessionAdaptor.java | 226 ++ .../qpid/client/AMQUndefinedDestination.java | 40 + .../apache/qpid/client/BasicMessageConsumer.java | 1079 ++++++++ .../qpid/client/BasicMessageConsumer_0_10.java | 409 +++ .../qpid/client/BasicMessageConsumer_0_8.java | 85 + .../apache/qpid/client/BasicMessageProducer.java | 561 ++++ .../qpid/client/BasicMessageProducer_0_10.java | 186 ++ .../qpid/client/BasicMessageProducer_0_8.java | 207 ++ .../java/org/apache/qpid/client/Closeable.java | 83 + .../qpid/client/ConnectionTuneParameters.java | 72 + .../org/apache/qpid/client/CustomJMSXProperty.java | 66 + .../org/apache/qpid/client/DispatcherCallback.java | 36 + .../org/apache/qpid/client/JMSAMQException.java | 65 + .../qpid/client/JmsNotImplementedException.java | 31 + .../apache/qpid/client/MessageConsumerPair.java | 43 + .../apache/qpid/client/QpidConnectionMetaData.java | 97 + .../apache/qpid/client/QueueReceiverAdaptor.java | 115 + .../org/apache/qpid/client/QueueSenderAdapter.java | 228 ++ .../org/apache/qpid/client/SSLConfiguration.java | 61 + .../apache/qpid/client/TemporaryDestination.java | 38 + .../apache/qpid/client/TopicPublisherAdapter.java | 205 ++ .../apache/qpid/client/TopicSubscriberAdaptor.java | 132 + .../org/apache/qpid/client/XAConnectionImpl.java | 78 + .../org/apache/qpid/client/XAResourceImpl.java | 529 ++++ .../java/org/apache/qpid/client/XASessionImpl.java | 159 ++ .../client/configuration/ClientProperties.java | 78 + .../qpid/client/failover/FailoverException.java | 49 + .../qpid/client/failover/FailoverHandler.java | 256 ++ .../qpid/client/failover/FailoverNoopSupport.java | 75 + .../failover/FailoverProtectedOperation.java | 49 + .../qpid/client/failover/FailoverRetrySupport.java | 104 + .../apache/qpid/client/failover/FailoverState.java | 64 + .../qpid/client/failover/FailoverSupport.java | 47 + .../handler/AccessRequestOkMethodHandler.java | 50 + .../client/handler/BasicCancelOkMethodHandler.java | 55 + .../client/handler/BasicDeliverMethodHandler.java | 54 + .../client/handler/BasicReturnMethodHandler.java | 58 + .../client/handler/ChannelCloseMethodHandler.java | 110 + .../handler/ChannelCloseOkMethodHandler.java | 49 + .../client/handler/ChannelFlowMethodHandler.java | 51 + .../client/handler/ChannelFlowOkMethodHandler.java | 52 + .../client/handler/ClientMethodDispatcherImpl.java | 529 ++++ .../handler/ClientMethodDispatcherImpl_0_9.java | 153 + .../handler/ClientMethodDispatcherImpl_8_0.java | 85 + .../handler/ConnectionCloseMethodHandler.java | 106 + .../handler/ConnectionOpenOkMethodHandler.java | 49 + .../handler/ConnectionRedirectMethodHandler.java | 71 + .../handler/ConnectionSecureMethodHandler.java | 70 + .../handler/ConnectionStartMethodHandler.java | 232 ++ .../handler/ConnectionTuneMethodHandler.java | 83 + .../handler/ExchangeBoundOkMethodHandler.java | 57 + .../client/handler/QueueDeleteOkMethodHandler.java | 57 + .../qpid/client/message/AMQMessageDelegate.java | 138 + .../client/message/AMQMessageDelegateFactory.java | 54 + .../client/message/AMQMessageDelegate_0_10.java | 972 +++++++ .../client/message/AMQMessageDelegate_0_8.java | 567 ++++ .../qpid/client/message/AbstractBytesMessage.java | 149 + .../client/message/AbstractBytesTypedMessage.java | 802 ++++++ .../qpid/client/message/AbstractJMSMessage.java | 534 ++++ .../client/message/AbstractJMSMessageFactory.java | 187 ++ .../qpid/client/message/CloseConsumerMessage.java | 43 + .../qpid/client/message/FiledTableSupport.java | 56 + .../qpid/client/message/JMSBytesMessage.java | 390 +++ .../client/message/JMSBytesMessageFactory.java | 44 + .../qpid/client/message/JMSHeaderAdapter.java | 552 ++++ .../apache/qpid/client/message/JMSMapMessage.java | 513 ++++ .../qpid/client/message/JMSMapMessageFactory.java | 42 + .../qpid/client/message/JMSObjectMessage.java | 177 ++ .../client/message/JMSObjectMessageFactory.java | 41 + .../qpid/client/message/JMSStreamMessage.java | 206 ++ .../client/message/JMSStreamMessageFactory.java | 40 + .../apache/qpid/client/message/JMSTextMessage.java | 185 ++ .../qpid/client/message/JMSTextMessageFactory.java | 42 + .../qpid/client/message/MessageConverter.java | 195 ++ .../apache/qpid/client/message/MessageFactory.java | 47 + .../client/message/MessageFactoryRegistry.java | 173 ++ .../apache/qpid/client/message/ReturnMessage.java | 47 + .../qpid/client/message/UnprocessedMessage.java | 53 + .../client/message/UnprocessedMessage_0_10.java | 53 + .../client/message/UnprocessedMessage_0_8.java | 163 ++ .../protocol/AMQIoTransportProtocolSession.java | 146 + .../qpid/client/protocol/AMQProtocolHandler.java | 856 ++++++ .../qpid/client/protocol/AMQProtocolSession.java | 545 ++++ .../protocol/BlockingMethodFrameListener.java | 136 + .../qpid/client/protocol/HeartbeatConfig.java | 61 + .../qpid/client/protocol/HeartbeatDiagnostics.java | 121 + .../protocol/ProtocolBufferMonitorFilter.java | 115 + .../qpid/client/security/AMQCallbackHandler.java | 30 + .../client/security/CallbackHandlerRegistry.java | 231 ++ .../security/CallbackHandlerRegistry.properties | 22 + .../qpid/client/security/DynamicSaslRegistrar.java | 210 ++ .../security/DynamicSaslRegistrar.properties | 20 + .../apache/qpid/client/security/JCAProvider.java | 72 + .../UsernameHashedPasswordCallbackHandler.java | 102 + .../security/UsernamePasswordCallbackHandler.java | 60 + .../security/amqplain/AmqPlainSaslClient.java | 105 + .../amqplain/AmqPlainSaslClientFactory.java | 63 + .../CRAMMD5HashedSaslClientFactory.java | 72 + .../state/AMQMethodNotImplementedException.java | 32 + .../org/apache/qpid/client/state/AMQState.java | 60 + .../qpid/client/state/AMQStateChangedEvent.java | 48 + .../apache/qpid/client/state/AMQStateListener.java | 26 + .../apache/qpid/client/state/AMQStateManager.java | 199 ++ .../client/state/StateAwareMethodListener.java | 38 + .../org/apache/qpid/client/state/StateWaiter.java | 129 + .../listener/SpecificMethodFrameListener.java | 41 + .../AMQNoTransportForProtocolException.java | 59 + .../transport/AMQTransportConnectionException.java | 43 + .../client/transport/ITransportConnection.java | 32 + .../transport/SocketTransportConnection.java | 131 + .../qpid/client/transport/TransportConnection.java | 335 +++ .../transport/VmPipeTransportConnection.java | 62 + .../java/org/apache/qpid/client/url/URLParser.java | 244 ++ .../org/apache/qpid/client/url/URLParser_0_10.java | 423 +++ .../apache/qpid/client/util/BlockingWaiter.java | 348 +++ .../client/util/FlowControllingBlockingQueue.java | 125 + .../vmbroker/AMQVMBrokerCreationException.java | 60 + .../apache/qpid/filter/ArithmeticExpression.java | 268 ++ .../org/apache/qpid/filter/BinaryExpression.java | 103 + .../org/apache/qpid/filter/BooleanExpression.java | 33 + .../apache/qpid/filter/ComparisonExpression.java | 589 ++++ .../org/apache/qpid/filter/ConstantExpression.java | 204 ++ .../java/org/apache/qpid/filter/Expression.java | 34 + .../org/apache/qpid/filter/JMSSelectorFilter.java | 70 + .../org/apache/qpid/filter/LogicExpression.java | 108 + .../java/org/apache/qpid/filter/MessageFilter.java | 27 + .../org/apache/qpid/filter/PropertyExpression.java | 279 ++ .../org/apache/qpid/filter/UnaryExpression.java | 321 +++ .../java/org/apache/qpid/jms/BrokerDetails.java | 103 + .../qpid/jms/ChannelLimitReachedException.java | 46 + .../main/java/org/apache/qpid/jms/Connection.java | 69 + .../org/apache/qpid/jms/ConnectionListener.java | 58 + .../java/org/apache/qpid/jms/ConnectionURL.java | 94 + .../java/org/apache/qpid/jms/FailoverPolicy.java | 314 +++ .../src/main/java/org/apache/qpid/jms/Message.java | 30 + .../java/org/apache/qpid/jms/MessageConsumer.java | 27 + .../java/org/apache/qpid/jms/MessageProducer.java | 57 + .../src/main/java/org/apache/qpid/jms/Session.java | 101 + .../java/org/apache/qpid/jms/TopicSubscriber.java | 32 + .../apache/qpid/jms/failover/FailoverMethod.java | 76 + .../jms/failover/FailoverRoundRobinServers.java | 273 ++ .../qpid/jms/failover/FailoverSingleServer.java | 166 ++ .../java/org/apache/qpid/jndi/Example.properties | 40 + .../java/org/apache/qpid/jndi/NameParserImpl.java | 37 + .../jndi/PropertiesFileInitialContextFactory.java | 359 +++ .../java/org/apache/qpid/jndi/ReadOnlyContext.java | 527 ++++ .../org/apache/qpid/naming/ReadOnlyContext.java | 509 ++++ .../java/org/apache/qpid/naming/jndi.properties | 40 + .../java/org/apache/qpid/nclient/JMSTestCase.java | 135 + .../apache/qpid/nclient/MessagePartListener.java | 46 + .../qpid/nclient/util/ByteBufferMessage.java | 190 ++ .../apache/qpid/nclient/util/MessageListener.java | 34 + .../nclient/util/MessagePartListenerAdapter.java | 88 + .../java/org/apache/qpid/njms/ExceptionHelper.java | 60 + .../IBMPerfTest/JNDIBindConnectionFactory.java | 185 ++ .../org/apache/qpid/IBMPerfTest/JNDIBindQueue.java | 213 ++ .../org/apache/qpid/IBMPerfTest/JNDIBindTopic.java | 212 ++ .../java/org/apache/qpid/IBMPerfTest/README.txt | 11 + .../java/org/apache/qpid/cluster/Client.java | 129 + .../org/apache/qpid/codec/BasicDeliverTest.java | 277 ++ .../java/org/apache/qpid/codec/Client.java | 133 + .../java/org/apache/qpid/codec/Server.java | 103 + .../config/AMQConnectionFactoryInitialiser.java | 35 + .../org/apache/qpid/config/AbstractConfig.java | 69 + .../qpid/config/ConnectionFactoryInitialiser.java | 29 + .../java/org/apache/qpid/config/Connector.java | 40 + .../org/apache/qpid/config/ConnectorConfig.java | 28 + .../config/JBossConnectionFactoryInitialiser.java | 111 + .../java/org/apache/qpid/flow/ChannelFlowTest.java | 112 + .../qpid/fragmentation/TestLargePublisher.java | 196 ++ .../qpid/fragmentation/TestLargeSubscriber.java | 167 ++ .../java/org/apache/qpid/headers/Listener.java | 117 + .../org/apache/qpid/headers/MessageFactory.java | 175 ++ .../java/org/apache/qpid/headers/Publisher.java | 133 + .../org/apache/qpid/jndi/referenceable/Bind.java | 273 ++ .../org/apache/qpid/jndi/referenceable/Lookup.java | 196 ++ .../org/apache/qpid/jndi/referenceable/Unbind.java | 166 ++ .../java/org/apache/qpid/latency/LatencyTest.java | 153 + .../java/org/apache/qpid/mina/AcceptorTest.java | 102 + .../org/apache/qpid/mina/BlockingAcceptorTest.java | 93 + .../java/org/apache/qpid/mina/WriterTest.java | 271 ++ .../org/apache/qpid/multiconsumer/AMQTest.java | 269 ++ .../org/apache/qpid/pubsub1/TestPublisher.java | 176 ++ .../org/apache/qpid/pubsub1/TestSubscriber.java | 122 + .../client/connection/TestManyConnections.java | 95 + .../PropertiesFileInitialContextFactoryTest.java | 153 + .../apache/qpid/test/unit/jndi/example.properties | 38 + .../java/org/apache/qpid/topic/Config.java | 243 ++ .../java/org/apache/qpid/topic/Listener.java | 141 + .../java/org/apache/qpid/topic/MessageFactory.java | 155 ++ .../java/org/apache/qpid/topic/Publisher.java | 175 ++ .../java/org/apache/qpid/transacted/Config.java | 110 + .../java/org/apache/qpid/transacted/Ping.java | 45 + .../java/org/apache/qpid/transacted/Pong.java | 45 + .../java/org/apache/qpid/transacted/Relay.java | 127 + .../java/org/apache/qpid/transacted/Start.java | 44 + .../org/apache/qpid/weblogic/ServiceProvider.java | 151 + .../qpid/weblogic/ServiceRequestingClient.java | 185 ++ .../vmpipe/support/VmPipeIdleStatusChecker.java | 125 + .../java/org/apache/qpid/client/AMQQueueTest.java | 42 + .../org/apache/qpid/client/MockAMQConnection.java | 94 + .../qpid/client/message/TestMessageHelper.java | 46 + .../client/protocol/AMQProtocolHandlerTest.java | 297 ++ .../apache/qpid/client/protocol/MockIoSession.java | 312 +++ .../unit/basic/FieldTableKeyEnumeratorTest.java | 96 + .../test/unit/basic/FieldTablePropertyTest.java | 62 + .../client/BrokerDetails/BrokerDetailsTest.java | 99 + .../ChannelCloseMethodHandlerNoCloseOk.java | 97 + .../client/channelclose/NoCloseOKStateManager.java | 65 + .../client/connectionurl/ConnectionURLTest.java | 532 ++++ .../client/destinationurl/DestinationURLTest.java | 185 ++ .../test/unit/client/message/BytesMessageTest.java | 569 ++++ .../test/unit/client/message/MapMessageTest.java | 383 +++ .../unit/client/message/StreamMessageTest.java | 623 +++++ .../test/unit/client/message/TextMessageTest.java | 300 ++ .../test/unit/message/MessageConverterTest.java | 140 + .../qpid/test/unit/message/NonQpidMessage.java | 411 +++ .../qpid/test/unit/message/TestAMQSession.java | 171 ++ .../org/apache/qpid/test/unit/tests.properties | 45 + RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.bat | 69 + RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.sh | 27 + RC6/qpid/java/client/test/bin/IBM-Publisher.bat | 62 + RC6/qpid/java/client/test/bin/IBM-Publisher.sh | 22 + RC6/qpid/java/client/test/bin/IBM-PutGet.bat | 62 + RC6/qpid/java/client/test/bin/IBM-PutGet.sh | 21 + RC6/qpid/java/client/test/bin/IBM-README.txt | 19 + RC6/qpid/java/client/test/bin/IBM-Receiver.bat | 62 + RC6/qpid/java/client/test/bin/IBM-Receiver.sh | 22 + RC6/qpid/java/client/test/bin/IBM-Sender.bat | 62 + RC6/qpid/java/client/test/bin/IBM-Sender.sh | 22 + RC6/qpid/java/client/test/bin/IBM-Subscriber.bat | 62 + RC6/qpid/java/client/test/bin/IBM-Subscriber.sh | 22 + RC6/qpid/java/client/test/bin/headersListener.sh | 22 + .../java/client/test/bin/headersListenerGroup.sh | 25 + RC6/qpid/java/client/test/bin/headersPublisher.sh | 22 + RC6/qpid/java/client/test/bin/run_many.sh | 30 + .../java/client/test/bin/serviceProvidingClient.sh | 24 + .../client/test/bin/serviceRequestingClient.sh | 27 + RC6/qpid/java/client/test/bin/testService.sh | 22 + RC6/qpid/java/client/test/bin/topicListener.sh | 23 + RC6/qpid/java/client/test/bin/topicPublisher.sh | 22 + RC6/qpid/java/client/test/etc/ApacheDS.properties | 24 + RC6/qpid/java/client/test/example_build.xml | 104 + RC6/qpid/java/common.xml | 268 ++ RC6/qpid/java/common/Composite.tpl | 336 +++ RC6/qpid/java/common/Constant.tpl | 35 + RC6/qpid/java/common/Enum.tpl | 57 + RC6/qpid/java/common/Invoker.tpl | 73 + RC6/qpid/java/common/MethodDelegate.tpl | 37 + RC6/qpid/java/common/Option.tpl | 41 + RC6/qpid/java/common/StructFactory.tpl | 60 + RC6/qpid/java/common/Type.tpl | 84 + RC6/qpid/java/common/bin/qpid-run | 264 ++ RC6/qpid/java/common/build.xml | 104 + RC6/qpid/java/common/codegen | 89 + RC6/qpid/java/common/etc/qpid-run.conf | 25 + RC6/qpid/java/common/etc/qpid-run.conf.dev | 26 + RC6/qpid/java/common/genutil.py | 255 ++ RC6/qpid/java/common/protocol-version.xml | 70 + RC6/qpid/java/common/readme.txt | 4 + RC6/qpid/java/common/src/main/java/common.bnd | 6 + .../java/common/src/main/java/log4j.properties | 31 + .../mina/common/FixedSizeByteBufferAllocator.java | 467 ++++ .../mina/common/support/DefaultIoFuture.java | 227 ++ .../common/support/IoServiceListenerSupport.java | 351 +++ .../mina/filter/WriteBufferFullExeception.java | 48 + .../mina/filter/WriteBufferLimitFilterBuilder.java | 272 ++ .../filter/codec/OurCumulativeProtocolDecoder.java | 197 ++ .../mina/filter/codec/QpidProtocolCodecFilter.java | 440 +++ .../socket/nio/MultiThreadSocketAcceptor.java | 547 ++++ .../socket/nio/MultiThreadSocketConnector.java | 486 ++++ .../socket/nio/MultiThreadSocketFilterChain.java | 67 + .../socket/nio/MultiThreadSocketIoProcessor.java | 1026 +++++++ .../nio/MultiThreadSocketSessionConfigImpl.java | 240 ++ .../socket/nio/MultiThreadSocketSessionImpl.java | 488 ++++ .../mina/transport/vmpipe/QpidVmPipeConnector.java | 151 + .../org/apache/qpid/AMQChannelClosedException.java | 41 + .../java/org/apache/qpid/AMQChannelException.java | 59 + .../apache/qpid/AMQConnectionClosedException.java | 44 + .../org/apache/qpid/AMQConnectionException.java | 70 + .../apache/qpid/AMQConnectionFailureException.java | 65 + .../org/apache/qpid/AMQDisconnectedException.java | 39 + .../main/java/org/apache/qpid/AMQException.java | 122 + .../apache/qpid/AMQInvalidArgumentException.java | 45 + .../apache/qpid/AMQInvalidRoutingKeyException.java | 39 + .../org/apache/qpid/AMQPInvalidClassException.java | 39 + .../java/org/apache/qpid/AMQProtocolException.java | 38 + .../java/org/apache/qpid/AMQTimeoutException.java | 39 + .../org/apache/qpid/AMQUndeliveredException.java | 54 + .../org/apache/qpid/AMQUnknownExchangeType.java | 43 + .../apache/qpid/AMQUnresolvedAddressException.java | 50 + .../main/java/org/apache/qpid/BrokerDetails.java | 138 + .../java/org/apache/qpid/BrokerDetailsImpl.java | 264 ++ .../main/java/org/apache/qpid/ConsoleOutput.java | 54 + .../src/main/java/org/apache/qpid/ErrorCode.java | 123 + .../src/main/java/org/apache/qpid/QpidConfig.java | 111 + .../main/java/org/apache/qpid/QpidException.java | 58 + .../main/java/org/apache/qpid/SecurityHelper.java | 71 + .../main/java/org/apache/qpid/SerialException.java | 40 + .../src/main/java/org/apache/qpid/ToyBroker.java | 188 ++ .../src/main/java/org/apache/qpid/ToyClient.java | 106 + .../src/main/java/org/apache/qpid/ToyExchange.java | 154 ++ .../src/main/java/org/apache/qpid/api/Message.java | 126 + .../org/apache/qpid/codec/AMQCodecFactory.java | 77 + .../java/org/apache/qpid/codec/AMQDecoder.java | 271 ++ .../java/org/apache/qpid/codec/AMQEncoder.java | 66 + .../org/apache/qpid/common/AMQPFilterTypes.java | 61 + .../org/apache/qpid/common/ClientProperties.java | 52 + .../org/apache/qpid/common/QpidProperties.java | 190 ++ .../org/apache/qpid/configuration/Configured.java | 44 + .../qpid/configuration/PropertyException.java | 43 + .../apache/qpid/configuration/PropertyUtils.java | 164 ++ .../src/main/java/org/apache/qpid/dtx/XidImpl.java | 250 ++ .../org/apache/qpid/exchange/ExchangeDefaults.java | 65 + .../main/java/org/apache/qpid/framing/AMQBody.java | 40 + .../java/org/apache/qpid/framing/AMQDataBlock.java | 63 + .../apache/qpid/framing/AMQDataBlockDecoder.java | 120 + .../apache/qpid/framing/AMQDataBlockEncoder.java | 61 + .../java/org/apache/qpid/framing/AMQFrame.java | 125 + .../qpid/framing/AMQFrameDecodingException.java | 47 + .../org/apache/qpid/framing/AMQMethodBody.java | 83 + .../apache/qpid/framing/AMQMethodBodyFactory.java | 45 + .../org/apache/qpid/framing/AMQMethodBodyImpl.java | 96 + .../qpid/framing/AMQMethodBodyInstanceFactory.java | 30 + .../org/apache/qpid/framing/AMQMethodFactory.java | 90 + .../qpid/framing/AMQProtocolClassException.java | 39 + .../qpid/framing/AMQProtocolHeaderException.java | 41 + .../qpid/framing/AMQProtocolInstanceException.java | 39 + .../qpid/framing/AMQProtocolVersionException.java | 39 + .../org/apache/qpid/framing/AMQShortString.java | 776 ++++++ .../qpid/framing/AMQShortStringTokenizer.java | 31 + .../main/java/org/apache/qpid/framing/AMQType.java | 795 ++++++ .../java/org/apache/qpid/framing/AMQTypeMap.java | 48 + .../org/apache/qpid/framing/AMQTypedValue.java | 116 + .../qpid/framing/BasicContentHeaderProperties.java | 834 ++++++ .../java/org/apache/qpid/framing/BodyFactory.java | 31 + .../framing/CommonContentHeaderProperties.java | 81 + .../apache/qpid/framing/CompositeAMQDataBlock.java | 78 + .../main/java/org/apache/qpid/framing/Content.java | 26 + .../java/org/apache/qpid/framing/ContentBody.java | 121 + .../apache/qpid/framing/ContentBodyFactory.java | 48 + .../org/apache/qpid/framing/ContentHeaderBody.java | 131 + .../qpid/framing/ContentHeaderBodyFactory.java | 50 + .../qpid/framing/ContentHeaderProperties.java | 60 + .../framing/ContentHeaderPropertiesFactory.java | 59 + .../org/apache/qpid/framing/DeferredDataBlock.java | 50 + .../apache/qpid/framing/EncodableAMQDataBlock.java | 35 + .../org/apache/qpid/framing/EncodingUtils.java | 1033 +++++++ .../java/org/apache/qpid/framing/FieldTable.java | 1187 ++++++++ .../org/apache/qpid/framing/FieldTableFactory.java | 38 + .../org/apache/qpid/framing/HeartbeatBody.java | 79 + .../apache/qpid/framing/HeartbeatBodyFactory.java | 31 + .../apache/qpid/framing/ProtocolInitiation.java | 195 ++ .../qpid/framing/SmallCompositeAMQDataBlock.java | 98 + .../qpid/framing/VersionSpecificRegistry.java | 198 ++ .../abstraction/AbstractMethodConverter.java | 47 + .../qpid/framing/abstraction/ContentChunk.java | 32 + .../framing/abstraction/MessagePublishInfo.java | 38 + .../abstraction/MessagePublishInfoConverter.java | 32 + .../ProtocolVersionMethodConverter.java | 32 + .../qpid/framing/amqp_0_9/AMQMethodBody_0_9.java | 209 ++ .../qpid/framing/amqp_0_9/MethodConverter_0_9.java | 172 ++ .../qpid/framing/amqp_8_0/AMQMethodBody_8_0.java | 209 ++ .../qpid/framing/amqp_8_0/MethodConverter_8_0.java | 151 + .../src/main/java/org/apache/qpid/pool/Event.java | 155 ++ .../src/main/java/org/apache/qpid/pool/Job.java | 192 ++ .../java/org/apache/qpid/pool/PoolingFilter.java | 491 ++++ .../org/apache/qpid/pool/ReadWriteJobQueue.java | 432 +++ .../org/apache/qpid/pool/ReadWriteRunnable.java | 27 + .../org/apache/qpid/pool/ReadWriteThreadModel.java | 102 + .../pool/ReferenceCountingExecutorService.java | 172 ++ .../java/org/apache/qpid/protocol/AMQConstant.java | 236 ++ .../org/apache/qpid/protocol/AMQMethodEvent.java | 95 + .../apache/qpid/protocol/AMQMethodListener.java | 70 + .../apache/qpid/protocol/AMQProtocolWriter.java | 43 + .../protocol/AMQVersionAwareProtocolSession.java | 64 + .../apache/qpid/protocol/ProtocolVersionAware.java | 53 + .../apache/qpid/security/AMQPCallbackHandler.java | 28 + .../qpid/security/CallbackHandlerRegistry.java | 94 + .../apache/qpid/security/DynamicSaslRegistrar.java | 80 + .../java/org/apache/qpid/security/JCAProvider.java | 44 + .../security/UsernamePasswordCallbackHandler.java | 60 + .../qpid/security/amqplain/AmqPlainSaslClient.java | 105 + .../amqplain/AmqPlainSaslClientFactory.java | 62 + .../org/apache/qpid/ssl/SSLContextFactory.java | 195 ++ .../java/org/apache/qpid/transport/Binary.java | 145 + .../java/org/apache/qpid/transport/Binding.java | 36 + .../org/apache/qpid/transport/ClientDelegate.java | 138 + .../java/org/apache/qpid/transport/Connection.java | 505 ++++ .../apache/qpid/transport/ConnectionDelegate.java | 98 + .../apache/qpid/transport/ConnectionException.java | 70 + .../apache/qpid/transport/ConnectionListener.java | 38 + .../main/java/org/apache/qpid/transport/Echo.java | 71 + .../main/java/org/apache/qpid/transport/Field.java | 83 + .../java/org/apache/qpid/transport/Future.java | 37 + .../java/org/apache/qpid/transport/Header.java | 92 + .../java/org/apache/qpid/transport/Method.java | 180 ++ .../apache/qpid/transport/ProtocolDelegate.java | 40 + .../org/apache/qpid/transport/ProtocolError.java | 83 + .../org/apache/qpid/transport/ProtocolEvent.java | 40 + .../org/apache/qpid/transport/ProtocolHeader.java | 116 + .../qpid/transport/ProtocolVersionException.java | 62 + .../main/java/org/apache/qpid/transport/Range.java | 125 + .../java/org/apache/qpid/transport/RangeSet.java | 147 + .../java/org/apache/qpid/transport/Receiver.java | 38 + .../java/org/apache/qpid/transport/Result.java | 30 + .../java/org/apache/qpid/transport/Sender.java | 38 + .../org/apache/qpid/transport/SenderException.java | 52 + .../org/apache/qpid/transport/ServerDelegate.java | 154 ++ .../java/org/apache/qpid/transport/Session.java | 799 ++++++ .../qpid/transport/SessionClosedException.java | 49 + .../org/apache/qpid/transport/SessionDelegate.java | 149 + .../apache/qpid/transport/SessionException.java | 61 + .../org/apache/qpid/transport/SessionListener.java | 40 + .../main/java/org/apache/qpid/transport/Sink.java | 136 + .../java/org/apache/qpid/transport/Struct.java | 142 + .../apache/qpid/transport/TransportException.java | 51 + .../qpid/transport/codec/AbstractDecoder.java | 470 ++++ .../qpid/transport/codec/AbstractEncoder.java | 620 +++++ .../org/apache/qpid/transport/codec/BBDecoder.java | 144 + .../org/apache/qpid/transport/codec/BBEncoder.java | 323 +++ .../org/apache/qpid/transport/codec/Decoder.java | 283 ++ .../org/apache/qpid/transport/codec/Encodable.java | 44 + .../org/apache/qpid/transport/codec/Encoder.java | 282 ++ .../apache/qpid/transport/network/Assembler.java | 226 ++ .../qpid/transport/network/ConnectionBinding.java | 83 + .../qpid/transport/network/Disassembler.java | 239 ++ .../org/apache/qpid/transport/network/Frame.java | 151 + .../qpid/transport/network/InputHandler.java | 204 ++ .../qpid/transport/network/NetworkDelegate.java | 42 + .../qpid/transport/network/NetworkEvent.java | 34 + .../transport/network/io/InputHandler_0_9.java | 130 + .../qpid/transport/network/io/IoAcceptor.java | 92 + .../qpid/transport/network/io/IoReceiver.java | 145 + .../apache/qpid/transport/network/io/IoSender.java | 286 ++ .../qpid/transport/network/io/IoTransport.java | 229 ++ .../qpid/transport/network/mina/MinaHandler.java | 274 ++ .../qpid/transport/network/mina/MinaSender.java | 81 + .../qpid/transport/network/nio/NioHandler.java | 140 + .../qpid/transport/network/nio/NioSender.java | 121 + .../qpid/transport/network/ssl/SSLReceiver.java | 179 ++ .../qpid/transport/network/ssl/SSLSender.java | 197 ++ .../org/apache/qpid/transport/util/Functions.java | 97 + .../org/apache/qpid/transport/util/Logger.java | 130 + .../apache/qpid/transport/util/SliceIterator.java | 59 + .../org/apache/qpid/transport/util/Waiter.java | 63 + .../java/org/apache/qpid/url/AMQBindingURL.java | 236 ++ .../main/java/org/apache/qpid/url/BindingURL.java | 59 + .../java/org/apache/qpid/url/BindingURLParser.java | 466 ++++ .../main/java/org/apache/qpid/url/URLHelper.java | 173 ++ .../org/apache/qpid/url/URLSyntaxException.java | 97 + .../org/apache/qpid/util/CommandLineParser.java | 689 +++++ .../ConcurrentLinkedMessageQueueAtomicSize.java | 258 ++ .../qpid/util/ConcurrentLinkedQueueAtomicSize.java | 70 + .../qpid/util/ConcurrentLinkedQueueNoSize.java | 38 + .../main/java/org/apache/qpid/util/FileUtils.java | 316 +++ .../java/org/apache/qpid/util/MessageQueue.java | 43 + .../java/org/apache/qpid/util/NameUUIDGen.java | 59 + .../org/apache/qpid/util/PrettyPrintingUtils.java | 75 + .../java/org/apache/qpid/util/PropertiesUtils.java | 200 ++ .../java/org/apache/qpid/util/RandomUUIDGen.java | 39 + .../src/main/java/org/apache/qpid/util/Serial.java | 108 + .../main/java/org/apache/qpid/util/Strings.java | 94 + .../main/java/org/apache/qpid/util/UUIDGen.java | 36 + .../src/main/java/org/apache/qpid/util/UUIDs.java | 59 + .../util/concurrent/AlreadyUnblockedException.java | 34 + .../qpid/util/concurrent/BatchSynchQueue.java | 122 + .../qpid/util/concurrent/BatchSynchQueueBase.java | 834 ++++++ .../apache/qpid/util/concurrent/BooleanLatch.java | 128 + .../org/apache/qpid/util/concurrent/Capacity.java | 35 + .../org/apache/qpid/util/concurrent/Condition.java | 50 + .../apache/qpid/util/concurrent/SynchBuffer.java | 50 + .../qpid/util/concurrent/SynchException.java | 52 + .../apache/qpid/util/concurrent/SynchQueue.java | 48 + .../apache/qpid/util/concurrent/SynchRecord.java | 74 + .../org/apache/qpid/util/concurrent/SynchRef.java | 51 + .../main/resources/org/apache/qpid/ssl/qpid.cert | Bin 0 -> 756 bytes .../apache/mina/SocketIOTest/IOWriterClient.java | 396 +++ .../apache/mina/SocketIOTest/IOWriterServer.java | 157 ++ .../java/org/apache/qpid/AMQExceptionTest.java | 106 + .../apache/qpid/framing/AMQShortStringTest.java | 109 + .../framing/BasicContentHeaderPropertiesTest.java | 188 ++ .../qpid/framing/PropertyFieldTableTest.java | 960 +++++++ .../org/apache/qpid/pool/PoolingFilterTest.java | 111 + .../java/org/apache/qpid/session/TestSession.java | 277 ++ .../org/apache/qpid/transport/ConnectionTest.java | 385 +++ .../java/org/apache/qpid/transport/GenTest.java | 44 + .../org/apache/qpid/transport/RangeSetTest.java | 238 ++ .../apache/qpid/util/CommandLineParserTest.java | 554 ++++ .../java/org/apache/qpid/util/FileUtilsTest.java | 522 ++++ .../test/java/org/apache/qpid/util/SerialTest.java | 82 + .../common/templates/method/MethodBodyInterface.vm | 62 + .../templates/method/version/MethodBodyClass.vm | 209 ++ .../model/ClientMethodDispatcherInterface.vm | 56 + .../templates/model/MethodDispatcherInterface.vm | 39 + .../common/templates/model/MethodRegistryClass.vm | 104 + .../templates/model/ProtocolVersionListClass.vm | 178 ++ .../model/ServerMethodDispatcherInterface.vm | 56 + .../templates/model/version/AmqpConstantsClass.vm | 37 + .../version/ClientMethodDispatcherInterface.vm | 55 + .../model/version/MethodDispatcherInterface.vm | 43 + .../templates/model/version/MethodRegistryClass.vm | 193 ++ .../version/ServerMethodDispatcherInterface.vm | 55 + RC6/qpid/java/common/templating.py | 119 + RC6/qpid/java/cpp.async.testprofile | 3 + RC6/qpid/java/cpp.cluster.testprofile | 5 + RC6/qpid/java/cpp.noprefetch.testprofile | 4 + RC6/qpid/java/cpp.testprofile | 3 + RC6/qpid/java/default-longrunning.testprofile | 1 + RC6/qpid/java/default.testprofile | 25 + RC6/qpid/java/doc/AMQBlazeDetailedDesign.vsd | Bin 0 -> 120320 bytes RC6/qpid/java/doc/FramingClassDiagram.vsd | Bin 0 -> 206848 bytes RC6/qpid/java/doc/broker-overview.dia | Bin 0 -> 1263 bytes RC6/qpid/java/etc/coding_standards.xml | 138 + RC6/qpid/java/etc/license_header.txt | 20 + RC6/qpid/java/etc/log4j.xml | 61 + RC6/qpid/java/integrationtests/README.txt | 13 + RC6/qpid/java/integrationtests/bin/interoptests.py | 180 ++ RC6/qpid/java/integrationtests/build.xml | 28 + .../docs/RunningSustainedTests.txt | 17 + .../integrationtests/jar-with-dependencies.xml | 47 + .../interop/clienttestcases/TestCase1DummyRun.java | 135 + .../interop/clienttestcases/TestCase2BasicP2P.java | 209 ++ .../clienttestcases/TestCase3BasicPubSub.java | 239 ++ .../clienttestcases/TestCase4P2PMessageSize.java | 214 ++ .../TestCase5PubSubMessageSize.java | 243 ++ .../testcases/InteropTestCase1DummyRun.java | 84 + .../testcases/InteropTestCase2BasicP2P.java | 90 + .../testcases/InteropTestCase3BasicPubSub.java | 88 + .../testcases/InteropTestCase4P2PMessageSize.java | 193 ++ .../InteropTestCase5PubSubMessageSize.java | 193 ++ .../qpid/sustained/SustainedClientTestCase.java | 906 ++++++ .../apache/qpid/sustained/SustainedTestCase.java | 126 + .../src/resources/sustained-log4j.xml | 69 + RC6/qpid/java/java.testprofile | 5 + .../qpid/junit/maven/IsolatedClassLoader.java | 113 + .../apache/qpid/junit/maven/TKTestRunnerMojo.java | 274 ++ .../qpid/junit/maven/TKTestScriptGenMojo.java | 148 + RC6/qpid/java/junit-toolkit/build.xml | 26 + .../junit/concurrency/DefaultThreadFactory.java | 48 + .../concurrency/PossibleDeadlockException.java | 46 + .../qpid/junit/concurrency/TestRunnable.java | 239 ++ .../junit/concurrency/ThreadTestCoordinator.java | 486 ++++ .../qpid/junit/concurrency/ThreadTestExample.java | 145 + .../org/apache/qpid/junit/concurrency/package.html | 28 + .../qpid/junit/extensions/AsymptoticTestCase.java | 303 ++ .../junit/extensions/AsymptoticTestDecorator.java | 170 ++ .../apache/qpid/junit/extensions/BaseThrottle.java | 98 + .../qpid/junit/extensions/BatchedThrottle.java | 94 + .../junit/extensions/DurationTestDecorator.java | 205 ++ .../qpid/junit/extensions/InstrumentedTest.java | 66 + .../qpid/junit/extensions/NullResultPrinter.java | 92 + .../ParameterVariationTestDecorator.java | 172 ++ .../qpid/junit/extensions/ScaledTestDecorator.java | 375 +++ .../qpid/junit/extensions/SetupTaskAware.java | 55 + .../qpid/junit/extensions/SetupTaskHandler.java | 92 + .../qpid/junit/extensions/ShutdownHookable.java | 42 + .../qpid/junit/extensions/SleepThrottle.java | 81 + .../apache/qpid/junit/extensions/TKTestResult.java | 625 +++++ .../apache/qpid/junit/extensions/TKTestRunner.java | 694 +++++ .../TestRunnerImprovedErrorHandling.java | 131 + .../qpid/junit/extensions/TestThreadAware.java | 49 + .../org/apache/qpid/junit/extensions/Throttle.java | 73 + .../qpid/junit/extensions/TimingController.java | 175 ++ .../junit/extensions/TimingControllerAware.java | 43 + .../extensions/WrappedSuiteTestDecorator.java | 134 + .../extensions/listeners/CSVTestListener.java | 532 ++++ .../extensions/listeners/ConsoleTestListener.java | 264 ++ .../junit/extensions/listeners/TKTestListener.java | 132 + .../extensions/listeners/XMLTestListener.java | 400 +++ .../qpid/junit/extensions/listeners/package.html | 27 + .../org/apache/qpid/junit/extensions/package.html | 33 + .../junit/extensions/util/CommandLineParser.java | 787 ++++++ .../extensions/util/ContextualProperties.java | 494 ++++ .../qpid/junit/extensions/util/MathUtils.java | 428 +++ .../junit/extensions/util/ParsedProperties.java | 390 +++ .../apache/qpid/junit/extensions/util/SizeOf.java | 94 + .../qpid/junit/extensions/util/StackQueue.java | 131 + .../extensions/util/TestContextProperties.java | 202 ++ .../qpid/junit/extensions/util/TestUtils.java | 54 + .../apache/qpid/junit/extensions/util/package.html | 27 + RC6/qpid/java/lib/backport-util-concurrent-2.2.jar | Bin 0 -> 326319 bytes RC6/qpid/java/lib/bnd-0.0.249.jar | Bin 0 -> 255605 bytes RC6/qpid/java/lib/cobertura/README.txt | 9 + RC6/qpid/java/lib/com.ibm.icu_3.4.4.jar | Bin 0 -> 3255246 bytes RC6/qpid/java/lib/commons-cli-1.0.jar | Bin 0 -> 30117 bytes RC6/qpid/java/lib/commons-codec-1.3.jar | Bin 0 -> 46725 bytes RC6/qpid/java/lib/commons-collections-3.2.jar | Bin 0 -> 571259 bytes RC6/qpid/java/lib/commons-configuration-1.2.jar | Bin 0 -> 163822 bytes RC6/qpid/java/lib/commons-lang-2.2.jar | Bin 0 -> 243016 bytes RC6/qpid/java/lib/commons-logging-1.0.4.jar | Bin 0 -> 38015 bytes RC6/qpid/java/lib/commons-pool-1.4.jar | Bin 0 -> 87077 bytes RC6/qpid/java/lib/geronimo-jms_1.1_spec-1.0.jar | Bin 0 -> 28211 bytes .../java/lib/geronimo-servlet_2.5_spec-1.2.jar | Bin 0 -> 70593 bytes RC6/qpid/java/lib/javacc.jar | Bin 0 -> 378781 bytes RC6/qpid/java/lib/jline-0.9.94.jar | Bin 0 -> 87325 bytes RC6/qpid/java/lib/junit-3.8.1.jar | Bin 0 -> 121070 bytes RC6/qpid/java/lib/junit-4.4.jar | Bin 0 -> 161477 bytes RC6/qpid/java/lib/jython-2.2-rc2.jar | Bin 0 -> 1203486 bytes RC6/qpid/java/lib/jython-lib.jar | Bin 0 -> 1570006 bytes RC6/qpid/java/lib/log4j-1.2.12.jar | Bin 0 -> 358085 bytes RC6/qpid/java/lib/mina-core-1.0.1.jar | Bin 0 -> 313338 bytes RC6/qpid/java/lib/mina-filter-ssl-1.0.1.jar | Bin 0 -> 28950 bytes .../java/lib/org.apache.felix.framework-1.0.0.jar | Bin 0 -> 320272 bytes .../java/lib/org.eclipse.core.commands_3.2.0.jar | Bin 0 -> 89522 bytes .../lib/org.eclipse.core.contenttype_3.2.0.jar | Bin 0 -> 76141 bytes .../lib/org.eclipse.core.expressions_3.2.0.jar | Bin 0 -> 66023 bytes RC6/qpid/java/lib/org.eclipse.core.jobs_3.2.0.jar | Bin 0 -> 74797 bytes ...lipse.core.runtime.compatibility.auth_3.2.0.jar | Bin 0 -> 18733 bytes ...e.core.runtime.compatibility.registry_3.2.0.jar | Bin 0 -> 7887 bytes .../java/lib/org.eclipse.core.runtime_3.2.0.jar | Bin 0 -> 76627 bytes .../java/lib/org.eclipse.equinox.common_3.2.0.jar | Bin 0 -> 79780 bytes .../META-INF/ECLIPSE.RSA | Bin 0 -> 3487 bytes .../META-INF/ECLIPSE.SF | 17 + .../META-INF/MANIFEST.MF | 25 + .../META-INF/eclipse.inf | 3 + .../about.html | 28 + .../eclipse_1115.so | Bin 0 -> 118028 bytes .../launcher.carbon.macosx.properties | 12 + .../META-INF/ECLIPSE.SF | 17 + .../META-INF/MANIFEST.MF | 25 + .../META-INF/eclipse.inf | 3 + .../about.html | 28 + .../launcher.gtk.linux.x86.properties | 12 + ...pse.equinox.launcher_1.0.101.R34x_v20080819.jar | Bin 0 -> 43682 bytes .../lib/org.eclipse.equinox.preferences_3.2.0.jar | Bin 0 -> 91662 bytes .../lib/org.eclipse.equinox.registry_3.2.0.jar | Bin 0 -> 143841 bytes RC6/qpid/java/lib/org.eclipse.help_3.2.0.jar | Bin 0 -> 115440 bytes ...ipse.jdt.launching.macosx_3.1.100.v20080422.jar | Bin 0 -> 61174 bytes RC6/qpid/java/lib/org.eclipse.jface_3.2.0.jar | Bin 0 -> 813664 bytes .../org.eclipse.osgi_3.4.2.R34x_v20080826-1230.jar | Bin 0 -> 997883 bytes .../org.eclipse.swt.carbon.macosx_3.4.1.v3449c.jar | Bin 0 -> 1852007 bytes .../org.eclipse.swt.gtk.linux.x86_3.4.1.v3449c.jar | Bin 0 -> 2006608 bytes .../lib/org.eclipse.swt.win32.win32.x86_3.2.0.jar | Bin 0 -> 1553273 bytes RC6/qpid/java/lib/org.eclipse.swt_3.4.1.v3449c.jar | Bin 0 -> 15807 bytes RC6/qpid/java/lib/org.eclipse.ui.forms_3.2.0.jar | Bin 0 -> 235372 bytes .../java/lib/org.eclipse.ui.workbench_3.2.1.jar | Bin 0 -> 3076013 bytes RC6/qpid/java/lib/org.eclipse.ui_3.2.0.jar | Bin 0 -> 124376 bytes RC6/qpid/java/lib/org.osgi.core_1.0.0.jar | Bin 0 -> 60929 bytes RC6/qpid/java/lib/slf4j-api-1.4.0.jar | Bin 0 -> 13095 bytes RC6/qpid/java/lib/slf4j-log4j12-1.4.0.jar | Bin 0 -> 7132 bytes RC6/qpid/java/lib/xalan-2.7.0.jar | Bin 0 -> 2730442 bytes RC6/qpid/java/log4j-test.xml | 53 + RC6/qpid/java/management/client/README.txt | 117 + RC6/qpid/java/management/client/bin/qman | 31 + RC6/qpid/java/management/client/build.xml | 66 + RC6/qpid/java/management/client/doc/man/qman | 17 + .../java/management/client/etc/qman-config.xml | 47 + RC6/qpid/java/management/client/etc/qman.log4j | 30 + .../java/org/apache/qpid/management/Messages.java | 119 + .../java/org/apache/qpid/management/Names.java | 57 + .../java/org/apache/qpid/management/Protocol.java | 47 + .../configuration/AccessModeMapping.java | 83 + .../BrokerAlreadyConnectedException.java | 53 + .../configuration/BrokerConnectionData.java | 270 ++ .../configuration/BrokerConnectionDataParser.java | 137 + .../configuration/BrokerConnectionException.java | 42 + .../management/configuration/Configuration.java | 383 +++ .../configuration/ConfigurationException.java | 51 + .../management/configuration/Configurator.java | 287 ++ .../qpid/management/configuration/IParser.java | 44 + .../configuration/MessageHandlerMapping.java | 90 + .../management/configuration/QpidDatasource.java | 249 ++ .../apache/qpid/management/configuration/Tag.java | 51 + .../qpid/management/configuration/TypeMapping.java | 90 + .../configuration/UnknownAccessCodeException.java | 53 + .../configuration/UnknownBrokerException.java | 43 + .../configuration/UnknownTypeCodeException.java | 53 + .../domain/handler/base/BaseMessageHandler.java | 54 + .../base/ContentIndicationMessageHandler.java | 114 + .../domain/handler/base/IMessageHandler.java | 52 + .../handler/impl/ConfigurationMessageHandler.java | 57 + .../handler/impl/EventContentMessageHandler.java | 51 + .../impl/HeartBeatIndicationMessageHandler.java | 39 + .../handler/impl/IMethodInvocationListener.java | 41 + .../impl/InstrumentationMessageHandler.java | 57 + .../domain/handler/impl/InvocationResult.java | 157 ++ .../impl/MethodOrEventDataTransferObject.java | 68 + .../handler/impl/MethodResponseMessageHandler.java | 106 + .../handler/impl/SchemaResponseMessageHandler.java | 217 ++ .../qpid/management/domain/model/AccessMode.java | 33 + .../qpid/management/domain/model/Direction.java | 33 + .../qpid/management/domain/model/DomainModel.java | 239 ++ .../qpid/management/domain/model/IValidator.java | 38 + .../management/domain/model/InvocationEvent.java | 76 + .../qpid/management/domain/model/JmxService.java | 348 +++ .../model/MissingFeatureAttributesException.java | 35 + .../qpid/management/domain/model/QpidArgument.java | 82 + .../management/domain/model/QpidAttribute.java | 105 + .../qpid/management/domain/model/QpidClass.java | 768 ++++++ .../qpid/management/domain/model/QpidEntity.java | 158 ++ .../qpid/management/domain/model/QpidEvent.java | 456 +++ .../qpid/management/domain/model/QpidFeature.java | 88 + .../domain/model/QpidFeatureBuilder.java | 454 +++ .../qpid/management/domain/model/QpidMethod.java | 147 + .../qpid/management/domain/model/QpidPackage.java | 279 ++ .../qpid/management/domain/model/QpidProperty.java | 295 ++ .../management/domain/model/QpidStatistic.java | 34 + .../model/UnableToBuildFeatureException.java | 51 + .../domain/model/ValidationException.java | 105 + .../qpid/management/domain/model/type/AbsTime.java | 44 + .../qpid/management/domain/model/type/Binary.java | 151 + .../qpid/management/domain/model/type/Boolean.java | 44 + .../management/domain/model/type/DeltaTime.java | 44 + .../qpid/management/domain/model/type/Double.java | 44 + .../qpid/management/domain/model/type/Float.java | 44 + .../qpid/management/domain/model/type/Int16.java | 44 + .../qpid/management/domain/model/type/Int32.java | 44 + .../qpid/management/domain/model/type/Int64.java | 44 + .../qpid/management/domain/model/type/Int8.java | 44 + .../qpid/management/domain/model/type/Map.java | 44 + .../domain/model/type/ObjectReference.java | 44 + .../qpid/management/domain/model/type/Str16.java | 44 + .../qpid/management/domain/model/type/Str8.java | 44 + .../qpid/management/domain/model/type/Type.java | 101 + .../qpid/management/domain/model/type/Uint16.java | 44 + .../qpid/management/domain/model/type/Uint32.java | 44 + .../qpid/management/domain/model/type/Uint64.java | 44 + .../qpid/management/domain/model/type/Uint8.java | 44 + .../qpid/management/domain/model/type/Uuid.java | 46 + .../domain/services/BrokerMessageListener.java | 177 ++ .../domain/services/ManagementClient.java | 231 ++ .../domain/services/MessageTokenizer.java | 152 + .../domain/services/MethodInvocationException.java | 50 + .../qpid/management/domain/services/QMan.java | 365 +++ .../management/domain/services/QpidService.java | 360 +++ .../domain/services/SequenceNumberGenerator.java | 41 + .../domain/services/StartupFailureException.java | 42 + .../domain/services/UnableToComplyException.java | 31 + .../apache/qpid/management/messages/AmqpCoDec.java | 178 ++ .../management/messages/ManagementMessage.java | 189 ++ .../messages/MethodInvocationRequestMessage.java | 161 ++ .../management/messages/SchemaRequestMessage.java | 68 + .../qpid/management/servlet/QManServlet.java | 66 + .../org/apache/qpid/management/TestConstants.java | 64 + .../configuration/ConfigurationTest.java | 229 ++ .../management/configuration/ConfiguratorTest.java | 164 ++ .../configuration/MappingParsersTest.java | 79 + .../base/ContentIndicationMessageHandlerTest.java | 59 + .../domain/model/BaseDomainModelTestCase.java | 44 + .../model/BaseQpidFeatureBuilderTestCase.java | 96 + .../management/domain/model/DomainModelTest.java | 55 + .../domain/model/OptionalPropertiesTest.java | 187 ++ .../management/domain/model/QpidClassTest.java | 408 +++ .../management/domain/model/QpidEventTest.java | 293 ++ .../domain/model/QpidMethodBuilderTest.java | 147 + .../domain/model/QpidNumberPropertyTest.java | 171 ++ .../management/domain/model/QpidPackageTest.java | 53 + .../domain/model/QpidPropertyBuilderTest.java | 269 ++ .../domain/model/QpidStatisticBuilderTest.java | 159 ++ .../domain/model/QpidStringPropertyTest.java | 127 + .../management/domain/model/type/BinaryTest.java | 59 + .../domain/services/BrokerMessageListenerTest.java | 241 ++ .../domain/services/MessageTokenizerTest.java | 140 + .../qpid/management/online/BaseOnlineTestCase.java | 58 + RC6/qpid/java/management/client/web.xml | 32 + .../management/eclipse-plugin/META-INF/MANIFEST.MF | 21 + RC6/qpid/java/management/eclipse-plugin/README.txt | 21 + .../java/management/eclipse-plugin/bin/qpidmc.bat | 55 + .../java/management/eclipse-plugin/bin/qpidmc.sh | 75 + .../management/eclipse-plugin/bin/qpidmc_gtk.sh | 26 + .../management/eclipse-plugin/bin/qpidmc_motif.sh | 21 + .../eclipse-plugin/build-release-common.properties | 38 + .../build-release-linux-gtk-x86.properties | 34 + .../eclipse-plugin/build-release-macosx.properties | 40 + .../eclipse-plugin/build-release-macosx.xml | 91 + .../build-release-win32-win32-x86.properties | 34 + .../management/eclipse-plugin/build-release.xml | 152 + RC6/qpid/java/management/eclipse-plugin/build.xml | 88 + .../java/management/eclipse-plugin/icons/Thumbs.db | Bin 0 -> 97280 bytes .../java/management/eclipse-plugin/icons/add.gif | Bin 0 -> 318 bytes .../management/eclipse-plugin/icons/delete.gif | Bin 0 -> 143 bytes .../eclipse-plugin/icons/icon_ClosedFolder.gif | Bin 0 -> 160 bytes .../eclipse-plugin/icons/icon_OpenFolder.gif | Bin 0 -> 152 bytes .../management/eclipse-plugin/icons/mbean_view.png | Bin 0 -> 2046 bytes .../eclipse-plugin/icons/notifications.gif | Bin 0 -> 104 bytes .../eclipse-plugin/icons/qpidConnections.gif | Bin 0 -> 168 bytes .../management/eclipse-plugin/icons/qpidmc.gif | Bin 0 -> 1225 bytes .../management/eclipse-plugin/icons/qpidmc16.gif | Bin 0 -> 928 bytes .../management/eclipse-plugin/icons/qpidmc32.bmp | Bin 0 -> 1139 bytes .../management/eclipse-plugin/icons/qpidmc32.gif | Bin 0 -> 1139 bytes .../management/eclipse-plugin/icons/reconnect.gif | Bin 0 -> 327 bytes .../management/eclipse-plugin/icons/refresh.gif | Bin 0 -> 182 bytes .../management/eclipse-plugin/icons/splash.bmp | Bin 0 -> 207078 bytes .../java/management/eclipse-plugin/icons/stop.gif | Bin 0 -> 215 bytes .../management/eclipse-plugin/plugin.properties | 20 + RC6/qpid/java/management/eclipse-plugin/plugin.xml | 223 ++ .../org/apache/qpid/management/ui/Activator.java | 84 + .../org/apache/qpid/management/ui/Application.java | 63 + .../management/ui/ApplicationActionBarAdvisor.java | 96 + .../qpid/management/ui/ApplicationRegistry.java | 136 + .../management/ui/ApplicationWorkbenchAdvisor.java | 46 + .../ui/ApplicationWorkbenchWindowAdvisor.java | 65 + .../org/apache/qpid/management/ui/Constants.java | 140 + .../org/apache/qpid/management/ui/ManagedBean.java | 132 + .../apache/qpid/management/ui/ManagedObject.java | 40 + .../apache/qpid/management/ui/ManagedServer.java | 103 + .../org/apache/qpid/management/ui/Perspective.java | 46 + .../apache/qpid/management/ui/ServerRegistry.java | 172 ++ .../qpid/management/ui/actions/AbstractAction.java | 146 + .../qpid/management/ui/actions/AddServer.java | 326 +++ .../management/ui/actions/CloseConnection.java | 56 + .../qpid/management/ui/actions/EditAttribute.java | 51 + .../management/ui/actions/ReconnectServer.java | 260 ++ .../apache/qpid/management/ui/actions/Refresh.java | 53 + .../qpid/management/ui/actions/RemoveServer.java | 50 + .../qpid/management/ui/actions/VersionAction.java | 94 + .../ui/exceptions/InfoRequiredException.java | 36 + .../ui/exceptions/ManagementConsoleException.java | 31 + .../qpid/management/ui/jmx/ClientListener.java | 77 + .../ui/jmx/ClientNotificationListener.java | 41 + .../qpid/management/ui/jmx/JMXManagedObject.java | 48 + .../qpid/management/ui/jmx/JMXServerRegistry.java | 714 +++++ .../qpid/management/ui/jmx/MBeanUtility.java | 466 ++++ .../qpid/management/ui/model/AttributeData.java | 96 + .../management/ui/model/ManagedAttributeModel.java | 118 + .../management/ui/model/NotificationInfoModel.java | 51 + .../management/ui/model/NotificationObject.java | 101 + .../qpid/management/ui/model/OperationData.java | 110 + .../management/ui/model/OperationDataModel.java | 72 + .../qpid/management/ui/model/ParameterData.java | 95 + .../ui/sasl/CRAMMD5HashedSaslClientFactory.java | 60 + .../qpid/management/ui/sasl/ClientSaslFactory.java | 54 + .../qpid/management/ui/sasl/JCAProvider.java | 56 + .../qpid/management/ui/sasl/PlainSaslClient.java | 203 ++ .../qpid/management/ui/sasl/SaslProvider.java | 35 + .../ui/sasl/UserPasswordCallbackHandler.java | 73 + .../UsernameHashedPasswordCallbackHandler.java | 82 + .../management/ui/views/AttributesTabControl.java | 936 +++++++ .../ui/views/ConnectionTypeTabControl.java | 59 + .../ui/views/ExchangeTypeTabControl.java | 60 + .../management/ui/views/INotificationViewer.java | 32 + .../management/ui/views/MBeanTypeTabControl.java | 336 +++ .../apache/qpid/management/ui/views/MBeanView.java | 545 ++++ .../qpid/management/ui/views/NavigationView.java | 1253 +++++++++ .../ui/views/NotificationsTabControl.java | 427 +++ .../management/ui/views/NumberVerifyListener.java | 46 + .../management/ui/views/OperationTabControl.java | 903 ++++++ .../management/ui/views/QueueTypeTabControl.java | 296 ++ .../qpid/management/ui/views/TabControl.java | 102 + .../qpid/management/ui/views/TreeObject.java | 126 + .../ui/views/VHNotificationsTabControl.java | 483 ++++ .../qpid/management/ui/views/ViewUtility.java | 593 ++++ .../src/main/resources/.eclipseproduct | 23 + .../eclipse-plugin/src/main/resources/eclipse.exe | Bin 0 -> 180224 bytes .../eclipse-plugin/src/main/resources/eclipse.ini | 23 + .../src/main/resources/icons/Console.icns | Bin 0 -> 51007 bytes .../src/main/resources/license.eclipse.txt | 88 + .../main/resources/macosx/Configuration/config.ini | 30 + .../src/main/resources/macosx/Info.plist | 31 + .../src/main/resources/macosx/eclipse | Bin 0 -> 59200 bytes .../src/main/resources/macosx/eclipse.ini | 14 + .../src/main/resources/sasl/MANIFEST.MF | 19 + .../eclipse-plugin/src/main/resources/startup.jar | Bin 0 -> 33049 bytes .../main/resources/unix/configuration/config.ini | 27 + .../src/main/resources/unix/eclipse.ini | 11 + .../main/resources/win32/configuration/config.ini | 26 + .../qpid/management/ui/ManagementConsoleTest.java | 126 + RC6/qpid/java/management/tools/qpid-cli/Guide.txt | 143 + RC6/qpid/java/management/tools/qpid-cli/LICENSE | 225 ++ RC6/qpid/java/management/tools/qpid-cli/NOTICE | 12 + RC6/qpid/java/management/tools/qpid-cli/README | 64 + .../java/management/tools/qpid-cli/bin/qpid-cli | 35 + .../management/tools/qpid-cli/bin/qpid-cli.bat | 27 + RC6/qpid/java/management/tools/qpid-cli/build.xml | 31 + .../java/management/tools/qpid-cli/report.property | 26 + .../qpid-cli/src/org/apache/qpid/Command.java | 62 + .../src/org/apache/qpid/CommandConstants.java | 50 + .../org/apache/qpid/CommandExecusionEngine.java | 88 + .../org/apache/qpid/CommandLineInterpreter.java | 194 ++ .../qpid-cli/src/org/apache/qpid/Connector.java | 76 + .../src/org/apache/qpid/ConnectorFactory.java | 71 + .../src/org/apache/qpid/ReportGenerator.java | 180 ++ .../src/org/apache/qpid/commands/Command.java | 113 + .../org/apache/qpid/commands/Commanddelete.java | 228 ++ .../src/org/apache/qpid/commands/Commandhelp.java | 73 + .../src/org/apache/qpid/commands/Commandinfo.java | 259 ++ .../src/org/apache/qpid/commands/Commandlist.java | 296 ++ .../src/org/apache/qpid/commands/Commandmove.java | 294 ++ .../src/org/apache/qpid/commands/Commandview.java | 276 ++ .../apache/qpid/commands/Commandviewcontent.java | 284 ++ .../apache/qpid/commands/objects/AllObjects.java | 44 + .../qpid/commands/objects/ConnectionObject.java | 54 + .../qpid/commands/objects/ExchangeObject.java | 53 + .../apache/qpid/commands/objects/ObjectNames.java | 477 ++++ .../apache/qpid/commands/objects/QueueObject.java | 86 + .../commands/objects/UserManagementObject.java | 45 + .../qpid/commands/objects/VirtualHostObject.java | 53 + .../org/apache/qpid/utils/CommandLineOption.java | 112 + .../qpid/utils/CommandLineOptionConstants.java | 53 + .../apache/qpid/utils/CommandLineOptionParser.java | 218 ++ .../org/apache/qpid/utils/JMXConfigProperty.java | 47 + .../org/apache/qpid/utils/JMXConfiguration.java | 148 + .../src/org/apache/qpid/utils/JMXinfo.java | 72 + .../qpid-cli/test/org/apache/qpid/AllTest.java | 80 + .../test/org/apache/qpid/ConnectionConstants.java | 51 + .../apache/qpid/TestCommandExecusionEngine.java | 90 + .../apache/qpid/TestCommandLineInterpreter.java | 101 + .../test/org/apache/qpid/TestConnector.java | 106 + .../test/org/apache/qpid/TestReportGenerator.java | 50 + .../test/org/apache/qpid/commands/TestCommand.java | 99 + .../apache/qpid/commands/TestCommanddelete.java | 99 + .../org/apache/qpid/commands/TestCommandinfo.java | 95 + .../org/apache/qpid/commands/TestCommandlist.java | 100 + .../org/apache/qpid/commands/TestCommandmove.java | 100 + .../org/apache/qpid/commands/TestCommandview.java | 96 + .../qpid/commands/TestCommandviewcontent.java | 99 + .../qpid/commands/objects/TestAllObject.java | 93 + .../commands/objects/TestConnectionObject.java | 105 + .../qpid/commands/objects/TestExchangeObject.java | 103 + .../qpid/commands/objects/TestObjectNames.java | 51 + .../qpid/commands/objects/TestQueueObject.java | 106 + .../commands/objects/TestUserManagementObject.java | 102 + .../commands/objects/TestVirtualHostObject.java | 88 + .../apache/qpid/utils/TestCommandLineOption.java | 85 + .../qpid/utils/TestCommandLineOptionParser.java | 100 + .../apache/qpid/utils/TestJMXConfigProperty.java | 51 + .../apache/qpid/utils/TestJMXConfiguration.java | 85 + .../test/org/apache/qpid/utils/TestJMXinfo.java | 53 + RC6/qpid/java/module.xml | 542 ++++ .../java/perftests/RunningPerformanceTests.txt | 141 + RC6/qpid/java/perftests/bin/run_many.sh | 30 + RC6/qpid/java/perftests/bin/topicListener.sh | 32 + RC6/qpid/java/perftests/bin/topicPublisher.sh | 31 + RC6/qpid/java/perftests/build.xml | 329 +++ RC6/qpid/java/perftests/dist-zip.xml | 45 + .../java/perftests/etc/jndi/activemq.properties | 20 + .../perftests/etc/jndi/failovertest.properties | 19 + .../java/perftests/etc/jndi/perftests.properties | 19 + .../java/perftests/etc/jndi/swiftmq.properties | 20 + RC6/qpid/java/perftests/etc/perftests.log4j | 46 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-1.sh | 21 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-2.sh | 21 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-3.sh | 21 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-4.sh | 21 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-5.sh | 21 + RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-6.sh | 21 + RC6/qpid/java/perftests/etc/scripts/Connections.sh | 20 + RC6/qpid/java/perftests/etc/scripts/JobQueue.sh | 20 + RC6/qpid/java/perftests/etc/scripts/Latency.sh | 21 + RC6/qpid/java/perftests/etc/scripts/MessageSize.sh | 20 + RC6/qpid/java/perftests/etc/scripts/PT-Qpid-13.sh | 42 + RC6/qpid/java/perftests/etc/scripts/PT-Qpid-14.sh | 41 + RC6/qpid/java/perftests/etc/scripts/Reliability.sh | 20 + RC6/qpid/java/perftests/etc/scripts/RunAll.sh | 25 + .../java/perftests/etc/scripts/Test-ActiveMQ.sh | 32 + .../java/perftests/etc/scripts/Test-SwiftMQ.sh | 30 + RC6/qpid/java/perftests/etc/scripts/Throughput.sh | 20 + .../perftests/etc/scripts/sendAndWaitClient.sh | 22 + RC6/qpid/java/perftests/generate-scripts | 74 + RC6/qpid/java/perftests/jar-with-dependencies.xml | 91 + RC6/qpid/java/perftests/scripts.xml | 328 +++ .../qpid/client/message/TestMessageFactory.java | 120 + .../config/AMQConnectionFactoryInitialiser.java | 35 + .../org/apache/qpid/config/AbstractConfig.java | 69 + .../qpid/config/ConnectionFactoryInitialiser.java | 29 + .../java/org/apache/qpid/config/Connector.java | 40 + .../org/apache/qpid/config/ConnectorConfig.java | 28 + .../config/JBossConnectionFactoryInitialiser.java | 112 + .../main/java/org/apache/qpid/oldtopic/Config.java | 243 ++ .../java/org/apache/qpid/oldtopic/Listener.java | 141 + .../org/apache/qpid/oldtopic/MessageFactory.java | 153 + .../java/org/apache/qpid/oldtopic/Publisher.java | 175 ++ .../org/apache/qpid/ping/PingAsyncTestPerf.java | 292 ++ .../main/java/org/apache/qpid/ping/PingClient.java | 107 + .../org/apache/qpid/ping/PingDurableClient.java | 452 +++ .../org/apache/qpid/ping/PingLatencyTestPerf.java | 311 +++ .../org/apache/qpid/ping/PingSendOnlyClient.java | 93 + .../java/org/apache/qpid/ping/PingTestPerf.java | 196 ++ .../apache/qpid/requestreply/PingPongBouncer.java | 453 +++ .../apache/qpid/requestreply/PingPongProducer.java | 1718 ++++++++++++ .../apache/qpid/requestreply/PingPongTestPerf.java | 251 ++ .../qpid/test/testcases/MessageThroughputPerf.java | 199 ++ .../main/java/org/apache/qpid/topic/Config.java | 326 +++ .../main/java/org/apache/qpid/topic/Listener.java | 303 ++ .../java/org/apache/qpid/topic/MessageFactory.java | 157 ++ .../main/java/org/apache/qpid/topic/Publisher.java | 186 ++ .../java/org/apache/qpid/plugins/JythonMojo.java | 100 + RC6/qpid/java/release-docs/RELEASE_NOTES.txt | 35 + RC6/qpid/java/resources/LICENSE | 731 +++++ RC6/qpid/java/resources/NOTICE | 48 + RC6/qpid/java/resources/README | 40 + RC6/qpid/java/systests/build.xml | 30 + RC6/qpid/java/systests/etc/bin/fail.py | 88 + RC6/qpid/java/systests/etc/bin/testclients.sh | 23 + .../vmpipe/support/VmPipeIdleStatusChecker.java | 125 + .../qpid/client/AMQQueueDeferredOrderingTest.java | 152 + .../org/apache/qpid/client/DispatcherTest.java | 252 ++ ...sageListenerMultiConsumerImmediatePrefetch.java | 44 + .../client/MessageListenerMultiConsumerTest.java | 245 ++ .../apache/qpid/client/MessageListenerTest.java | 179 ++ .../MultipleJCAProviderRegistrationTest.java | 103 + .../qpid/client/ResetMessageListenerTest.java | 229 ++ .../qpid/client/message/NonQpidObjectMessage.java | 236 ++ .../qpid/server/AMQBrokerManagerMBeanTest.java | 92 + .../java/org/apache/qpid/server/ack/TxAckTest.java | 262 ++ .../exchange/AbstractHeadersExchangeTestBase.java | 562 ++++ .../qpid/server/exchange/HeadersExchangeTest.java | 106 + .../exchange/MessagingTestConfigProperties.java | 308 +++ .../ReturnUnroutableMandatoryMessageTest.java | 320 +++ .../qpid/server/failover/FailoverMethodTest.java | 154 ++ .../apache/qpid/server/failure/HeapExhaustion.java | 236 ++ .../org/apache/qpid/server/plugins/PluginTest.java | 54 + .../protocol/AMQProtocolSessionMBeanTest.java | 124 + .../qpid/server/protocol/MaxChannelsTest.java | 70 + .../apache/qpid/server/protocol/MockIoSession.java | 297 ++ .../java/org/apache/qpid/server/queue/AckTest.java | 420 +++ .../qpid/server/queue/MockProtocolSession.java | 262 ++ .../qpid/server/queue/PersistentTestManual.java | 276 ++ .../org/apache/qpid/server/queue/PriorityTest.java | 235 ++ .../server/queue/QueueDepthWithSelectorTest.java | 258 ++ .../qpid/server/queue/SubscriptionTestHelper.java | 227 ++ .../apache/qpid/server/queue/TimeToLiveTest.java | 213 ++ .../qpid/server/security/acl/SimpleACLTest.java | 656 +++++ .../qpid/server/store/SkeletonMessageStore.java | 155 ++ .../apache/qpid/server/store/SlowMessageStore.java | 297 ++ .../qpid/server/store/TestMemoryMessageStore.java | 51 + .../qpid/server/store/TestReferenceCounting.java | 169 ++ .../org/apache/qpid/server/txn/TxnBufferTest.java | 306 ++ .../org/apache/qpid/server/util/AveragedRun.java | 66 + .../java/org/apache/qpid/server/util/RunStats.java | 57 + .../java/org/apache/qpid/server/util/TimedRun.java | 52 + .../org/apache/qpid/test/client/CancelTest.java | 95 + .../org/apache/qpid/test/client/DupsOkTest.java | 164 ++ .../apache/qpid/test/client/FlowControlTest.java | 209 ++ .../qpid/test/client/QueueBrowserAutoAckTest.java | 537 ++++ .../test/client/QueueBrowserClientAckTest.java | 37 + .../qpid/test/client/QueueBrowserDupsOkTest.java | 34 + .../qpid/test/client/QueueBrowserNoAckTest.java | 36 + .../qpid/test/client/QueueBrowserPreAckTest.java | 35 + .../test/client/QueueBrowserTransactedTest.java | 34 + .../qpid/test/client/failover/FailoverTest.java | 272 ++ .../test/client/message/MessageToStringTest.java | 251 ++ .../test/client/timeouts/SyncWaitDelayTest.java | 125 + .../client/timeouts/SyncWaitTimeoutDelayTest.java | 72 + .../apache/qpid/test/framework/AMQPPublisher.java | 54 + .../org/apache/qpid/test/framework/Assertion.java | 39 + .../apache/qpid/test/framework/AssertionBase.java | 66 + .../qpid/test/framework/BrokerLifecycleAware.java | 70 + .../apache/qpid/test/framework/CauseFailure.java | 42 + .../test/framework/CauseFailureUserPrompt.java | 65 + .../org/apache/qpid/test/framework/Circuit.java | 109 + .../org/apache/qpid/test/framework/CircuitEnd.java | 91 + .../apache/qpid/test/framework/CircuitEndBase.java | 152 + .../org/apache/qpid/test/framework/DropInTest.java | 51 + .../qpid/test/framework/ExceptionMonitor.java | 205 ++ .../qpid/test/framework/FrameworkBaseCase.java | 285 ++ .../test/framework/FrameworkClientBaseCase.java | 31 + .../qpid/test/framework/FrameworkTestContext.java | 48 + .../test/framework/LocalAMQPCircuitFactory.java | 168 ++ .../qpid/test/framework/LocalCircuitFactory.java | 316 +++ .../qpid/test/framework/MessageIdentityVector.java | 167 ++ .../apache/qpid/test/framework/MessageMonitor.java | 105 + .../framework/MessagingTestConfigProperties.java | 685 +++++ .../test/framework/NotApplicableAssertion.java | 112 + .../org/apache/qpid/test/framework/Publisher.java | 74 + .../org/apache/qpid/test/framework/Receiver.java | 92 + .../apache/qpid/test/framework/TestCaseVector.java | 88 + .../qpid/test/framework/TestClientDetails.java | 86 + .../org/apache/qpid/test/framework/TestUtils.java | 192 ++ .../clocksynch/ClockSynchFailureException.java | 45 + .../framework/clocksynch/ClockSynchThread.java | 124 + .../framework/clocksynch/ClockSynchronizer.java | 69 + .../clocksynch/LocalClockSynchronizer.java | 73 + .../framework/clocksynch/UDPClockReference.java | 165 ++ .../framework/clocksynch/UDPClockSynchronizer.java | 463 ++++ .../distributedcircuit/DistributedCircuitImpl.java | 469 ++++ .../DistributedPublisherImpl.java | 95 + .../DistributedReceiverImpl.java | 95 + .../distributedcircuit/TestClientCircuitEnd.java | 324 +++ .../framework/distributedtesting/Coordinator.java | 539 ++++ .../DistributedTestDecorator.java | 166 ++ .../distributedtesting/FanOutTestDecorator.java | 244 ++ .../distributedtesting/InteropTestDecorator.java | 209 ++ .../distributedtesting/OptOutTestCase.java | 69 + .../framework/distributedtesting/TestClient.java | 497 ++++ .../TestClientControlledTest.java | 108 + .../test/framework/listeners/XMLTestListener.java | 399 +++ .../localcircuit/LocalAMQPPublisherImpl.java | 133 + .../framework/localcircuit/LocalCircuitImpl.java | 306 ++ .../framework/localcircuit/LocalPublisherImpl.java | 170 ++ .../framework/localcircuit/LocalReceiverImpl.java | 144 + .../org/apache/qpid/test/framework/package.html | 43 + .../test/framework/qpid/AMQPFeatureDecorator.java | 96 + .../test/framework/qpid/CauseFailureDecorator.java | 95 + .../qpid/test/framework/qpid/CauseFailureInVM.java | 70 + .../test/framework/qpid/InVMBrokerDecorator.java | 135 + .../framework/sequencers/BaseCircuitFactory.java | 136 + .../test/framework/sequencers/CircuitFactory.java | 102 + .../framework/sequencers/FanOutCircuitFactory.java | 198 ++ .../sequencers/InteropCircuitFactory.java | 152 + .../apache/qpid/test/testcases/FailoverTest.java | 119 + .../qpid/test/testcases/ImmediateMessageTest.java | 303 ++ .../qpid/test/testcases/MandatoryMessageTest.java | 321 +++ .../apache/qpid/test/testcases/RollbackTest.java | 132 + .../org/apache/qpid/test/testcases/TTLTest.java | 151 + .../apache/qpid/test/unit/ack/AcknowledgeTest.java | 160 ++ .../org/apache/qpid/test/unit/ack/RecoverTest.java | 331 +++ .../qpid/test/unit/basic/BytesMessageTest.java | 282 ++ .../test/unit/basic/FieldTableMessageTest.java | 163 ++ .../test/unit/basic/InvalidDestinationTest.java | 104 + .../qpid/test/unit/basic/LargeMessageTest.java | 184 ++ .../qpid/test/unit/basic/MapMessageTest.java | 1271 +++++++++ .../test/unit/basic/MultipleConnectionTest.java | 230 ++ .../qpid/test/unit/basic/ObjectMessageTest.java | 278 ++ .../qpid/test/unit/basic/PropertyValueTest.java | 408 +++ .../test/unit/basic/PubSubTwoConnectionTest.java | 75 + .../apache/qpid/test/unit/basic/ReceiveTest.java | 82 + .../apache/qpid/test/unit/basic/SelectorTest.java | 306 ++ .../qpid/test/unit/basic/SessionStartTest.java | 115 + .../qpid/test/unit/basic/TextMessageTest.java | 248 ++ .../qpid/test/unit/basic/close/CloseTest.java | 70 + .../qpid/test/unit/client/AMQConnectionTest.java | 251 ++ .../qpid/test/unit/client/AMQSessionTest.java | 110 + .../client/channelclose/ChannelCloseOkTest.java | 241 ++ .../unit/client/channelclose/ChannelCloseTest.java | 418 +++ .../channelclose/CloseWithBlockingReceiveTest.java | 81 + .../client/connection/ConnectionCloseTest.java | 108 + .../client/connection/ConnectionStartTest.java | 158 ++ .../unit/client/connection/ConnectionTest.java | 286 ++ .../client/connection/ExceptionListenerTest.java | 62 + .../qpid/test/unit/client/forwardall/Client.java | 133 + .../test/unit/client/forwardall/CombinedTest.java | 69 + .../qpid/test/unit/client/forwardall/Service.java | 94 + .../unit/client/forwardall/ServiceCreator.java | 112 + .../test/unit/client/forwardall/SpecialQueue.java | 46 + .../unit/client/message/ObjectMessageTest.java | 335 +++ .../client/protocol/AMQProtocolSessionTest.java | 120 + .../client/temporaryqueue/TemporaryQueueTest.java | 258 ++ .../qpid/test/unit/close/CloseBeforeAckTest.java | 142 + .../qpid/test/unit/close/MessageRequeueTest.java | 372 +++ .../test/unit/close/TopicPublisherCloseTest.java | 69 + .../qpid/test/unit/ct/DurableSubscriberTest.java | 167 ++ .../qpid/test/unit/message/JMSDestinationTest.java | 89 + .../qpid/test/unit/message/JMSPropertiesTest.java | 135 + .../qpid/test/unit/message/StreamMessageTest.java | 161 ++ .../java/org/apache/qpid/test/unit/message/UTF8En | 4 + .../java/org/apache/qpid/test/unit/message/UTF8Jp | 4 + .../apache/qpid/test/unit/message/UTF8Test.java | 122 + .../test/unit/topic/DurableSubscriptionTest.java | 416 +++ .../qpid/test/unit/topic/TopicPublisherTest.java | 76 + .../qpid/test/unit/topic/TopicSessionTest.java | 419 +++ .../test/unit/transacted/CommitRollbackTest.java | 572 ++++ .../qpid/test/unit/transacted/TransactedTest.java | 348 +++ .../qpid/test/unit/xa/AbstractXATestCase.java | 132 + .../org/apache/qpid/test/unit/xa/FaultTest.java | 385 +++ .../org/apache/qpid/test/unit/xa/QueueTest.java | 657 +++++ .../org/apache/qpid/test/unit/xa/TopicTest.java | 1711 ++++++++++++ .../qpid/test/utils/ConversationFactory.java | 480 ++++ .../apache/qpid/test/utils/FailoverBaseCase.java | 93 + .../qpid/test/utils/QpidClientConnection.java | 289 ++ .../test/utils/QpidClientConnectionHelper.java | 295 ++ .../org/apache/qpid/test/utils/QpidTestCase.java | 597 ++++ .../apache/qpid/test/utils/ReflectionUtils.java | 228 ++ .../qpid/test/utils/ReflectionUtilsException.java | 44 + .../qpid/test/utils/protocol/TestIoSession.java | 104 + .../org/apache/qpid/util/ClasspathScanner.java | 234 ++ .../java/systests/src/main/java/systests.log4j | 28 + .../exchange/HeadersExchangePerformanceTest.java | 184 ++ .../server/protocol/TestProtocolInitiation.java | 266 ++ .../qpid/server/queue/QueueConcurrentPerfTest.java | 49 + .../apache/qpid/server/queue/QueuePerfTest.java | 258 ++ .../org/apache/qpid/server/queue/SendPerfTest.java | 181 ++ .../apache/qpid/server/util/ConcurrentTest.java | 79 + .../test/unit/ack/DisconnectAndRedeliverTest.java | 215 ++ .../tasks/src/org/apache/qpid/tasks/BaseTask.java | 74 + .../tasks/src/org/apache/qpid/tasks/Foreach.java | 84 + .../java/tasks/src/org/apache/qpid/tasks/Map.java | 94 + .../tasks/src/org/apache/qpid/tasks/Require.java | 80 + RC6/qpid/java/test-provider.properties | 38 + RC6/qpid/java/testkit/README | 153 + RC6/qpid/java/testkit/bin/perf_report.sh | 100 + RC6/qpid/java/testkit/bin/run_pub.sh | 24 + RC6/qpid/java/testkit/bin/run_soak_client.sh | 70 + RC6/qpid/java/testkit/bin/run_sub.sh | 25 + RC6/qpid/java/testkit/bin/setenv.sh | 49 + RC6/qpid/java/testkit/bin/soak_report.sh | 161 ++ RC6/qpid/java/testkit/build.xml | 27 + RC6/qpid/java/testkit/etc/jndi.properties | 35 + RC6/qpid/java/testkit/etc/test.log4j | 28 + .../org/apache/qpid/testkit/MessageFactory.java | 64 + .../org/apache/qpid/testkit/perf/LatencyTest.java | 332 +++ .../org/apache/qpid/testkit/perf/PerfBase.java | 102 + .../org/apache/qpid/testkit/perf/PerfConsumer.java | 248 ++ .../org/apache/qpid/testkit/perf/PerfProducer.java | 207 ++ .../org/apache/qpid/testkit/perf/TestParams.java | 160 ++ .../org/apache/qpid/testkit/soak/BaseTest.java | 152 + .../qpid/testkit/soak/MultiThreadedConsumer.java | 153 + .../qpid/testkit/soak/MultiThreadedProducer.java | 166 ++ .../apache/qpid/testkit/soak/ResourceLeakTest.java | 138 + .../apache/qpid/testkit/soak/SimpleConsumer.java | 134 + .../apache/qpid/testkit/soak/SimpleProducer.java | 146 + RC6/qpid/java/tools/bin/qpid-bench | 35 + RC6/qpid/java/tools/build.xml | 27 + .../main/java/org/apache/qpid/tools/JNDICheck.java | 200 ++ .../main/java/org/apache/qpid/tools/QpidBench.java | 857 ++++++ 1678 files changed, 244285 insertions(+) create mode 100644 RC6/qpid/java/010ExcludeList create mode 100644 RC6/qpid/java/010ExcludeList-noPrefetch create mode 100644 RC6/qpid/java/010ExcludeList-store create mode 100644 RC6/qpid/java/08ExcludeList create mode 100644 RC6/qpid/java/08ExcludeList-nonvm create mode 100644 RC6/qpid/java/ExcludeList create mode 100644 RC6/qpid/java/KEYS create mode 100644 RC6/qpid/java/XAExcludeList create mode 100644 RC6/qpid/java/broker-plugins/MANIFEST.MF create mode 100644 RC6/qpid/java/broker-plugins/build.xml create mode 100644 RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/Activator.java create mode 100644 RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java create mode 100644 RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java create mode 100644 RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java create mode 100644 RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java create mode 100755 RC6/qpid/java/broker/bin/msTool.sh create mode 100755 RC6/qpid/java/broker/bin/qpid-passwd create mode 100755 RC6/qpid/java/broker/bin/qpid-server create mode 100755 RC6/qpid/java/broker/bin/qpid-server-bdb.bat create mode 100755 RC6/qpid/java/broker/bin/qpid-server.bat create mode 100755 RC6/qpid/java/broker/bin/qpid.start create mode 100755 RC6/qpid/java/broker/bin/qpid.stop create mode 100755 RC6/qpid/java/broker/bin/qpid.stopall create mode 100644 RC6/qpid/java/broker/build.xml create mode 100644 RC6/qpid/java/broker/etc/access create mode 100644 RC6/qpid/java/broker/etc/acl.config.xml create mode 100644 RC6/qpid/java/broker/etc/config.xml create mode 100644 RC6/qpid/java/broker/etc/debug.log4j.xml create mode 100644 RC6/qpid/java/broker/etc/jmxremote.access create mode 100644 RC6/qpid/java/broker/etc/log4j.xml create mode 100644 RC6/qpid/java/broker/etc/md5passwd create mode 100644 RC6/qpid/java/broker/etc/mstool-log4j.xml create mode 100644 RC6/qpid/java/broker/etc/passwd create mode 100644 RC6/qpid/java/broker/etc/passwdVhost create mode 100644 RC6/qpid/java/broker/etc/persistent_config.xml create mode 100644 RC6/qpid/java/broker/etc/qpid-server.conf create mode 100644 RC6/qpid/java/broker/etc/qpid-server.conf.jpp create mode 100644 RC6/qpid/java/broker/etc/qpid.passwd create mode 100644 RC6/qpid/java/broker/etc/transient_config.xml create mode 100644 RC6/qpid/java/broker/etc/virtualhosts.xml create mode 100755 RC6/qpid/java/broker/python-test.xml create mode 100644 RC6/qpid/java/broker/src/main/grammar/SelectorParser.jj create mode 100644 RC6/qpid/java/broker/src/main/java/log4j.properties create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ExtractResendAndRequeue.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/Activator.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/UnauthorizedAccessException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java create mode 100755 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/HashedUser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreClosedException.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java create mode 100644 RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java create mode 100644 RC6/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java create mode 100644 RC6/qpid/java/build.deps create mode 100644 RC6/qpid/java/build.xml create mode 100755 RC6/qpid/java/clean-dir create mode 100644 RC6/qpid/java/client-java14/README.txt create mode 100644 RC6/qpid/java/client-java14/etc/sasl.properties create mode 100644 RC6/qpid/java/client-java14/src/main/assembly/client-java14-bin.xml create mode 100644 RC6/qpid/java/client-java14/src/main/assembly/jar-with-dependencies.xml create mode 100644 RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java create mode 100644 RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java create mode 100644 RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java create mode 100644 RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java create mode 100644 RC6/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java create mode 100644 RC6/qpid/java/client/build.xml create mode 100644 RC6/qpid/java/client/example/bin/README.txt create mode 100644 RC6/qpid/java/client/example/bin/set_classpath.bat create mode 100755 RC6/qpid/java/client/example/bin/set_classpath.sh create mode 100644 RC6/qpid/java/client/example/bin/verify_all create mode 100644 RC6/qpid/java/client/example/build.xml create mode 100644 RC6/qpid/java/client/example/source-jar.xml create mode 100644 RC6/qpid/java/client/example/src/main/java/README.txt create mode 100644 RC6/qpid/java/client/example/src/main/java/log4j.xml create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DeclareQueue.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DirectProducer.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/Listener.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/DeclareQueue.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/FannoutProducer.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/Listener.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicListener.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicPublisher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Consumer.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Listener.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Producer.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/direct.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java.in create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Consumer.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Listener.java create mode 100755 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Producer.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/fanout.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Listener.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Publisher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/pubsub.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Client.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Server.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/requestResponse.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java.in create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/QueueToTopic.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/transacted.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Client.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Server.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java create mode 100644 RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java create mode 100755 RC6/qpid/java/client/example/src/main/java/runSample.sh create mode 100644 RC6/qpid/java/client/src/main/grammar/SelectorParser.jj create mode 100755 RC6/qpid/java/client/src/main/java/client.bnd create mode 100644 RC6/qpid/java/client/src/main/java/client.log4j create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java create mode 100755 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/CloseConsumerMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser_0_10.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ArithmeticExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BinaryExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BooleanExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ComparisonExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ConstantExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/Expression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/LogicExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/MessageFilter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/UnaryExpression.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/Connection.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/Message.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/Session.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/TopicSubscriber.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/Example.properties create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/ReadOnlyContext.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/jndi.properties create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/MessagePartListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessageListener.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java create mode 100644 RC6/qpid/java/client/src/main/java/org/apache/qpid/njms/ExceptionHelper.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Client.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Server.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/Connector.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Config.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java create mode 100644 RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java create mode 100644 RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/tests.properties create mode 100644 RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.sh create mode 100644 RC6/qpid/java/client/test/bin/IBM-Publisher.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-Publisher.sh create mode 100644 RC6/qpid/java/client/test/bin/IBM-PutGet.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-PutGet.sh create mode 100644 RC6/qpid/java/client/test/bin/IBM-README.txt create mode 100644 RC6/qpid/java/client/test/bin/IBM-Receiver.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-Receiver.sh create mode 100644 RC6/qpid/java/client/test/bin/IBM-Sender.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-Sender.sh create mode 100644 RC6/qpid/java/client/test/bin/IBM-Subscriber.bat create mode 100755 RC6/qpid/java/client/test/bin/IBM-Subscriber.sh create mode 100755 RC6/qpid/java/client/test/bin/headersListener.sh create mode 100755 RC6/qpid/java/client/test/bin/headersListenerGroup.sh create mode 100755 RC6/qpid/java/client/test/bin/headersPublisher.sh create mode 100755 RC6/qpid/java/client/test/bin/run_many.sh create mode 100755 RC6/qpid/java/client/test/bin/serviceProvidingClient.sh create mode 100755 RC6/qpid/java/client/test/bin/serviceRequestingClient.sh create mode 100755 RC6/qpid/java/client/test/bin/testService.sh create mode 100755 RC6/qpid/java/client/test/bin/topicListener.sh create mode 100755 RC6/qpid/java/client/test/bin/topicPublisher.sh create mode 100644 RC6/qpid/java/client/test/etc/ApacheDS.properties create mode 100644 RC6/qpid/java/client/test/example_build.xml create mode 100644 RC6/qpid/java/common.xml create mode 100644 RC6/qpid/java/common/Composite.tpl create mode 100644 RC6/qpid/java/common/Constant.tpl create mode 100644 RC6/qpid/java/common/Enum.tpl create mode 100644 RC6/qpid/java/common/Invoker.tpl create mode 100644 RC6/qpid/java/common/MethodDelegate.tpl create mode 100644 RC6/qpid/java/common/Option.tpl create mode 100644 RC6/qpid/java/common/StructFactory.tpl create mode 100644 RC6/qpid/java/common/Type.tpl create mode 100755 RC6/qpid/java/common/bin/qpid-run create mode 100644 RC6/qpid/java/common/build.xml create mode 100755 RC6/qpid/java/common/codegen create mode 100644 RC6/qpid/java/common/etc/qpid-run.conf create mode 100644 RC6/qpid/java/common/etc/qpid-run.conf.dev create mode 100644 RC6/qpid/java/common/genutil.py create mode 100644 RC6/qpid/java/common/protocol-version.xml create mode 100644 RC6/qpid/java/common/readme.txt create mode 100755 RC6/qpid/java/common/src/main/java/common.bnd create mode 100644 RC6/qpid/java/common/src/main/java/log4j.properties create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQChannelException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetails.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ErrorCode.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/QpidConfig.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/QpidException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/SecurityHelper.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/SerialException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ToyBroker.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ToyClient.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ToyExchange.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/api/Message.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/configuration/Configured.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQType.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/Content.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/Event.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/Job.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Binary.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Binding.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Echo.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Field.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Future.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Header.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Method.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Range.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Receiver.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Result.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Sender.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/SenderException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Sink.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/Struct.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/TransportException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/Frame.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURL.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/url/URLHelper.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/FileUtils.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/Serial.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/Strings.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/UUIDs.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java create mode 100644 RC6/qpid/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java create mode 100644 RC6/qpid/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/session/TestSession.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/transport/GenTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java create mode 100644 RC6/qpid/java/common/src/test/java/org/apache/qpid/util/SerialTest.java create mode 100644 RC6/qpid/java/common/templates/method/MethodBodyInterface.vm create mode 100644 RC6/qpid/java/common/templates/method/version/MethodBodyClass.vm create mode 100644 RC6/qpid/java/common/templates/model/ClientMethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templates/model/MethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templates/model/MethodRegistryClass.vm create mode 100644 RC6/qpid/java/common/templates/model/ProtocolVersionListClass.vm create mode 100644 RC6/qpid/java/common/templates/model/ServerMethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templates/model/version/AmqpConstantsClass.vm create mode 100644 RC6/qpid/java/common/templates/model/version/ClientMethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templates/model/version/MethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templates/model/version/MethodRegistryClass.vm create mode 100644 RC6/qpid/java/common/templates/model/version/ServerMethodDispatcherInterface.vm create mode 100644 RC6/qpid/java/common/templating.py create mode 100644 RC6/qpid/java/cpp.async.testprofile create mode 100644 RC6/qpid/java/cpp.cluster.testprofile create mode 100644 RC6/qpid/java/cpp.noprefetch.testprofile create mode 100644 RC6/qpid/java/cpp.testprofile create mode 100644 RC6/qpid/java/default-longrunning.testprofile create mode 100644 RC6/qpid/java/default.testprofile create mode 100644 RC6/qpid/java/doc/AMQBlazeDetailedDesign.vsd create mode 100644 RC6/qpid/java/doc/FramingClassDiagram.vsd create mode 100644 RC6/qpid/java/doc/broker-overview.dia create mode 100644 RC6/qpid/java/etc/coding_standards.xml create mode 100644 RC6/qpid/java/etc/license_header.txt create mode 100644 RC6/qpid/java/etc/log4j.xml create mode 100644 RC6/qpid/java/integrationtests/README.txt create mode 100755 RC6/qpid/java/integrationtests/bin/interoptests.py create mode 100644 RC6/qpid/java/integrationtests/build.xml create mode 100644 RC6/qpid/java/integrationtests/docs/RunningSustainedTests.txt create mode 100644 RC6/qpid/java/integrationtests/jar-with-dependencies.xml create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java create mode 100644 RC6/qpid/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java create mode 100644 RC6/qpid/java/integrationtests/src/resources/sustained-log4j.xml create mode 100644 RC6/qpid/java/java.testprofile create mode 100644 RC6/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/IsolatedClassLoader.java create mode 100644 RC6/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestRunnerMojo.java create mode 100644 RC6/qpid/java/junit-toolkit-maven-plugin/src/main/org/apache/qpid/junit/maven/TKTestScriptGenMojo.java create mode 100644 RC6/qpid/java/junit-toolkit/build.xml create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/DefaultThreadFactory.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/PossibleDeadlockException.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/TestRunnable.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestCoordinator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/ThreadTestExample.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/concurrency/package.html create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestCase.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/AsymptoticTestDecorator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BaseThrottle.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/BatchedThrottle.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/DurationTestDecorator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/InstrumentedTest.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/NullResultPrinter.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ParameterVariationTestDecorator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ScaledTestDecorator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskAware.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SetupTaskHandler.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/ShutdownHookable.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/SleepThrottle.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestResult.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TKTestRunner.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestRunnerImprovedErrorHandling.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TestThreadAware.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/Throttle.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingController.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/TimingControllerAware.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/WrappedSuiteTestDecorator.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/CSVTestListener.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/ConsoleTestListener.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/TKTestListener.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/XMLTestListener.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/listeners/package.html create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/package.html create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/CommandLineParser.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ContextualProperties.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/MathUtils.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/ParsedProperties.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/SizeOf.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/StackQueue.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestContextProperties.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/TestUtils.java create mode 100644 RC6/qpid/java/junit-toolkit/src/main/org/apache/qpid/junit/extensions/util/package.html create mode 100644 RC6/qpid/java/lib/backport-util-concurrent-2.2.jar create mode 100644 RC6/qpid/java/lib/bnd-0.0.249.jar create mode 100644 RC6/qpid/java/lib/cobertura/README.txt create mode 100644 RC6/qpid/java/lib/com.ibm.icu_3.4.4.jar create mode 100644 RC6/qpid/java/lib/commons-cli-1.0.jar create mode 100644 RC6/qpid/java/lib/commons-codec-1.3.jar create mode 100644 RC6/qpid/java/lib/commons-collections-3.2.jar create mode 100644 RC6/qpid/java/lib/commons-configuration-1.2.jar create mode 100644 RC6/qpid/java/lib/commons-lang-2.2.jar create mode 100644 RC6/qpid/java/lib/commons-logging-1.0.4.jar create mode 100644 RC6/qpid/java/lib/commons-pool-1.4.jar create mode 100644 RC6/qpid/java/lib/geronimo-jms_1.1_spec-1.0.jar create mode 100644 RC6/qpid/java/lib/geronimo-servlet_2.5_spec-1.2.jar create mode 100644 RC6/qpid/java/lib/javacc.jar create mode 100644 RC6/qpid/java/lib/jline-0.9.94.jar create mode 100644 RC6/qpid/java/lib/junit-3.8.1.jar create mode 100644 RC6/qpid/java/lib/junit-4.4.jar create mode 100644 RC6/qpid/java/lib/jython-2.2-rc2.jar create mode 100644 RC6/qpid/java/lib/jython-lib.jar create mode 100644 RC6/qpid/java/lib/log4j-1.2.12.jar create mode 100755 RC6/qpid/java/lib/mina-core-1.0.1.jar create mode 100755 RC6/qpid/java/lib/mina-filter-ssl-1.0.1.jar create mode 100755 RC6/qpid/java/lib/org.apache.felix.framework-1.0.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.commands_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.contenttype_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.expressions_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.jobs_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.runtime.compatibility.auth_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.runtime.compatibility.registry_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.core.runtime_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.common_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/META-INF/ECLIPSE.RSA create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/META-INF/ECLIPSE.SF create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/META-INF/MANIFEST.MF create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/META-INF/eclipse.inf create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/about.html create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/eclipse_1115.so create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/launcher.carbon.macosx.properties create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/META-INF/ECLIPSE.SF create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/META-INF/MANIFEST.MF create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/META-INF/eclipse.inf create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/about.html create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/launcher.gtk.linux.x86.properties create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.launcher_1.0.101.R34x_v20080819.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.preferences_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.equinox.registry_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.help_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.jdt.launching.macosx_3.1.100.v20080422.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.jface_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.osgi_3.4.2.R34x_v20080826-1230.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.swt.carbon.macosx_3.4.1.v3449c.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.swt.gtk.linux.x86_3.4.1.v3449c.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.swt.win32.win32.x86_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.swt_3.4.1.v3449c.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.ui.forms_3.2.0.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.ui.workbench_3.2.1.jar create mode 100644 RC6/qpid/java/lib/org.eclipse.ui_3.2.0.jar create mode 100755 RC6/qpid/java/lib/org.osgi.core_1.0.0.jar create mode 100644 RC6/qpid/java/lib/slf4j-api-1.4.0.jar create mode 100644 RC6/qpid/java/lib/slf4j-log4j12-1.4.0.jar create mode 100644 RC6/qpid/java/lib/xalan-2.7.0.jar create mode 100644 RC6/qpid/java/log4j-test.xml create mode 100644 RC6/qpid/java/management/client/README.txt create mode 100755 RC6/qpid/java/management/client/bin/qman create mode 100644 RC6/qpid/java/management/client/build.xml create mode 100644 RC6/qpid/java/management/client/doc/man/qman create mode 100644 RC6/qpid/java/management/client/etc/qman-config.xml create mode 100644 RC6/qpid/java/management/client/etc/qman.log4j create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/Messages.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/Names.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/Protocol.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/AccessModeMapping.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/BrokerAlreadyConnectedException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/BrokerConnectionData.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/BrokerConnectionDataParser.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/BrokerConnectionException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/Configuration.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/ConfigurationException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/Configurator.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/IParser.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/MessageHandlerMapping.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/QpidDatasource.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/Tag.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/TypeMapping.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/UnknownAccessCodeException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/UnknownBrokerException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/configuration/UnknownTypeCodeException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/base/BaseMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/base/ContentIndicationMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/base/IMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/ConfigurationMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/EventContentMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/HeartBeatIndicationMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/IMethodInvocationListener.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/InstrumentationMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/InvocationResult.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/MethodOrEventDataTransferObject.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/MethodResponseMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/handler/impl/SchemaResponseMessageHandler.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/AccessMode.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/Direction.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/DomainModel.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/IValidator.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/InvocationEvent.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/JmxService.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/MissingFeatureAttributesException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidArgument.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidAttribute.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidClass.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidEntity.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidEvent.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidFeature.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidFeatureBuilder.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidMethod.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidPackage.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidProperty.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/QpidStatistic.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/UnableToBuildFeatureException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/ValidationException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/AbsTime.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Binary.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Boolean.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/DeltaTime.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Double.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Float.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Int16.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Int32.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Int64.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Int8.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Map.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/ObjectReference.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Str16.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Str8.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Type.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Uint16.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Uint32.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Uint64.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Uint8.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/model/type/Uuid.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/BrokerMessageListener.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/ManagementClient.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/MessageTokenizer.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/MethodInvocationException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/QMan.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/QpidService.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/SequenceNumberGenerator.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/StartupFailureException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/domain/services/UnableToComplyException.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/messages/AmqpCoDec.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/messages/ManagementMessage.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/messages/MethodInvocationRequestMessage.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/messages/SchemaRequestMessage.java create mode 100644 RC6/qpid/java/management/client/src/main/java/org/apache/qpid/management/servlet/QManServlet.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/TestConstants.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/configuration/ConfigurationTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/configuration/ConfiguratorTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/configuration/MappingParsersTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/handler/base/ContentIndicationMessageHandlerTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/BaseDomainModelTestCase.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/BaseQpidFeatureBuilderTestCase.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/DomainModelTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/OptionalPropertiesTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidClassTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidEventTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidMethodBuilderTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidNumberPropertyTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidPackageTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidPropertyBuilderTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidStatisticBuilderTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/QpidStringPropertyTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/model/type/BinaryTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/services/BrokerMessageListenerTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/domain/services/MessageTokenizerTest.java create mode 100644 RC6/qpid/java/management/client/src/test/java/org/apache/qpid/management/online/BaseOnlineTestCase.java create mode 100644 RC6/qpid/java/management/client/web.xml create mode 100644 RC6/qpid/java/management/eclipse-plugin/META-INF/MANIFEST.MF create mode 100644 RC6/qpid/java/management/eclipse-plugin/README.txt create mode 100644 RC6/qpid/java/management/eclipse-plugin/bin/qpidmc.bat create mode 100755 RC6/qpid/java/management/eclipse-plugin/bin/qpidmc.sh create mode 100755 RC6/qpid/java/management/eclipse-plugin/bin/qpidmc_gtk.sh create mode 100755 RC6/qpid/java/management/eclipse-plugin/bin/qpidmc_motif.sh create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release-common.properties create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release-linux-gtk-x86.properties create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release-macosx.properties create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release-macosx.xml create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release-win32-win32-x86.properties create mode 100644 RC6/qpid/java/management/eclipse-plugin/build-release.xml create mode 100644 RC6/qpid/java/management/eclipse-plugin/build.xml create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/Thumbs.db create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/add.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/delete.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/icon_ClosedFolder.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/icon_OpenFolder.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/mbean_view.png create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/notifications.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/qpidConnections.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/qpidmc.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/qpidmc16.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/qpidmc32.bmp create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/qpidmc32.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/reconnect.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/refresh.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/splash.bmp create mode 100644 RC6/qpid/java/management/eclipse-plugin/icons/stop.gif create mode 100644 RC6/qpid/java/management/eclipse-plugin/plugin.properties create mode 100644 RC6/qpid/java/management/eclipse-plugin/plugin.xml create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/.eclipseproduct create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/eclipse.exe create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/eclipse.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/icons/Console.icns create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Configuration/config.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/macosx/Info.plist create mode 100755 RC6/qpid/java/management/eclipse-plugin/src/main/resources/macosx/eclipse create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/macosx/eclipse.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/startup.jar create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/unix/eclipse.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini create mode 100644 RC6/qpid/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/Guide.txt create mode 100644 RC6/qpid/java/management/tools/qpid-cli/LICENSE create mode 100644 RC6/qpid/java/management/tools/qpid-cli/NOTICE create mode 100644 RC6/qpid/java/management/tools/qpid-cli/README create mode 100755 RC6/qpid/java/management/tools/qpid-cli/bin/qpid-cli create mode 100755 RC6/qpid/java/management/tools/qpid-cli/bin/qpid-cli.bat create mode 100644 RC6/qpid/java/management/tools/qpid-cli/build.xml create mode 100644 RC6/qpid/java/management/tools/qpid-cli/report.property create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/Command.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/CommandConstants.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/CommandExecusionEngine.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/CommandLineInterpreter.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/Connector.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/ConnectorFactory.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/ReportGenerator.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Command.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commanddelete.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandhelp.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandinfo.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandlist.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandmove.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandview.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/Commandviewcontent.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/CommandLineOption.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/CommandLineOptionConstants.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/CommandLineOptionParser.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/JMXConfigProperty.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/JMXConfiguration.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/utils/JMXinfo.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/AllTest.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/ConnectionConstants.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/TestCommandExecusionEngine.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/TestCommandLineInterpreter.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/TestConnector.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/TestReportGenerator.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommand.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommanddelete.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommandinfo.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommandlist.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommandmove.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommandview.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/TestCommandviewcontent.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestAllObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestConnectionObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestExchangeObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestObjectNames.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestQueueObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestUserManagementObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/commands/objects/TestVirtualHostObject.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/utils/TestCommandLineOption.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/utils/TestCommandLineOptionParser.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/utils/TestJMXConfigProperty.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/utils/TestJMXConfiguration.java create mode 100644 RC6/qpid/java/management/tools/qpid-cli/test/org/apache/qpid/utils/TestJMXinfo.java create mode 100644 RC6/qpid/java/module.xml create mode 100644 RC6/qpid/java/perftests/RunningPerformanceTests.txt create mode 100755 RC6/qpid/java/perftests/bin/run_many.sh create mode 100755 RC6/qpid/java/perftests/bin/topicListener.sh create mode 100755 RC6/qpid/java/perftests/bin/topicPublisher.sh create mode 100644 RC6/qpid/java/perftests/build.xml create mode 100644 RC6/qpid/java/perftests/dist-zip.xml create mode 100644 RC6/qpid/java/perftests/etc/jndi/activemq.properties create mode 100644 RC6/qpid/java/perftests/etc/jndi/failovertest.properties create mode 100644 RC6/qpid/java/perftests/etc/jndi/perftests.properties create mode 100644 RC6/qpid/java/perftests/etc/jndi/swiftmq.properties create mode 100644 RC6/qpid/java/perftests/etc/perftests.log4j create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-1.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-2.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-3.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-4.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-5.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/CTQ-Qpid-6.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/Connections.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/JobQueue.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/Latency.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/MessageSize.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/PT-Qpid-13.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/PT-Qpid-14.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/Reliability.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/RunAll.sh create mode 100644 RC6/qpid/java/perftests/etc/scripts/Test-ActiveMQ.sh create mode 100644 RC6/qpid/java/perftests/etc/scripts/Test-SwiftMQ.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/Throughput.sh create mode 100755 RC6/qpid/java/perftests/etc/scripts/sendAndWaitClient.sh create mode 100644 RC6/qpid/java/perftests/generate-scripts create mode 100644 RC6/qpid/java/perftests/jar-with-dependencies.xml create mode 100644 RC6/qpid/java/perftests/scripts.xml create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/Connector.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/topic/Config.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java create mode 100644 RC6/qpid/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java create mode 100644 RC6/qpid/java/plugins/src/main/java/org/apache/qpid/plugins/JythonMojo.java create mode 100644 RC6/qpid/java/release-docs/RELEASE_NOTES.txt create mode 100644 RC6/qpid/java/resources/LICENSE create mode 100644 RC6/qpid/java/resources/NOTICE create mode 100644 RC6/qpid/java/resources/README create mode 100644 RC6/qpid/java/systests/build.xml create mode 100644 RC6/qpid/java/systests/etc/bin/fail.py create mode 100755 RC6/qpid/java/systests/etc/bin/testclients.sh create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/DispatcherTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/MessageListenerTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/MultipleJCAProviderRegistrationTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/ResetMessageListenerTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/client/message/NonQpidObjectMessage.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/MessagingTestConfigProperties.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/failover/FailoverMethodTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/plugins/PluginTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/SimpleACLTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/store/TestMemoryMessageStore.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/CancelTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserAutoAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserClientAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserDupsOkTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserNoAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserPreAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTransactedTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/failover/FailoverTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/MessageToStringTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitDelayTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/client/timeouts/SyncWaitTimeoutDelayTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/BrokerLifecycleAware.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailure.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CauseFailureUserPrompt.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkTestContext.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageIdentityVector.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestCaseVector.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/package.html create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/CauseFailureInVM.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/FailoverTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/testcases/TTLTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/AcknowledgeTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ack/RecoverTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/MapMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/ReceiveTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SelectorTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/SessionStartTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/TextMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/basic/close/CloseTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/AMQSessionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionCloseTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/connection/ExceptionListenerTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Client.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/Service.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/protocol/AMQProtocolSessionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/ct/DurableSubscriberTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/StreamMessageTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/UTF8En create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/UTF8Jp create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/message/UTF8Test.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactedTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/AbstractXATestCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/FaultTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/QueueTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/xa/TopicTest.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/ConversationFactory.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/FailoverBaseCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidClientConnection.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidClientConnectionHelper.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/ReflectionUtils.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/ReflectionUtilsException.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/protocol/TestIoSession.java create mode 100644 RC6/qpid/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java create mode 100644 RC6/qpid/java/systests/src/main/java/systests.log4j create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java create mode 100644 RC6/qpid/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java create mode 100644 RC6/qpid/java/tasks/src/org/apache/qpid/tasks/BaseTask.java create mode 100644 RC6/qpid/java/tasks/src/org/apache/qpid/tasks/Foreach.java create mode 100644 RC6/qpid/java/tasks/src/org/apache/qpid/tasks/Map.java create mode 100644 RC6/qpid/java/tasks/src/org/apache/qpid/tasks/Require.java create mode 100644 RC6/qpid/java/test-provider.properties create mode 100644 RC6/qpid/java/testkit/README create mode 100644 RC6/qpid/java/testkit/bin/perf_report.sh create mode 100644 RC6/qpid/java/testkit/bin/run_pub.sh create mode 100644 RC6/qpid/java/testkit/bin/run_soak_client.sh create mode 100644 RC6/qpid/java/testkit/bin/run_sub.sh create mode 100644 RC6/qpid/java/testkit/bin/setenv.sh create mode 100644 RC6/qpid/java/testkit/bin/soak_report.sh create mode 100644 RC6/qpid/java/testkit/build.xml create mode 100644 RC6/qpid/java/testkit/etc/jndi.properties create mode 100644 RC6/qpid/java/testkit/etc/test.log4j create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/MessageFactory.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/perf/LatencyTest.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/perf/PerfBase.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/perf/PerfConsumer.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/perf/PerfProducer.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/perf/TestParams.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/BaseTest.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/MultiThreadedConsumer.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/MultiThreadedProducer.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/ResourceLeakTest.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/SimpleConsumer.java create mode 100644 RC6/qpid/java/testkit/src/main/java/org/apache/qpid/testkit/soak/SimpleProducer.java create mode 100644 RC6/qpid/java/tools/bin/qpid-bench create mode 100644 RC6/qpid/java/tools/build.xml create mode 100644 RC6/qpid/java/tools/src/main/java/org/apache/qpid/tools/JNDICheck.java create mode 100644 RC6/qpid/java/tools/src/main/java/org/apache/qpid/tools/QpidBench.java (limited to 'RC6/qpid/java') diff --git a/RC6/qpid/java/010ExcludeList b/RC6/qpid/java/010ExcludeList new file mode 100644 index 0000000000..7512329220 --- /dev/null +++ b/RC6/qpid/java/010ExcludeList @@ -0,0 +1,57 @@ +org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* +org.apache.qpid.client.ResetMessageListenerTest#* +// those tests should be run with prefetch off +org.apache.qpid.client.MessageListenerMultiConsumerTest#testRecieveC2Only +org.apache.qpid.client.MessageListenerMultiConsumerTest#testRecieveBoth +org.apache.qpid.test.unit.xa.TopicTest#testMigrateDurableSubscriber +org.apache.qpid.test.unit.ack.AcknowledgeTest#* +// those tests need durable subscribe states to be persisted +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testDurSubRestoredAfterNonPersistentMessageSent +// those tests require broker recovery +org.apache.qpid.test.unit.ct.DurableSubscriberTest#* +org.apache.qpid.test.unit.xa.TopicTest#testDurSubCrash +org.apache.qpid.test.unit.xa.TopicTest#testMultiMessagesDurSubCrash +org.apache.qpid.test.unit.xa.TopicTest#testRecover +org.apache.qpid.test.unit.xa.QueueTest#testRecover +org.apache.qpid.test.unit.xa.QueueTest#testSendAndRecover +//These tests are for the java broker +org.apache.qpid.server.security.acl.SimpleACLTest#* +org.apache.qpid.server.plugins.PluginTest#* +// This test is not finished +org.apache.qpid.test.testcases.TTLTest#* +org.apache.qpid.test.client.failover.FailoverTest#test4MinuteFailover +// Those tests are testing 0.8 specific semantics +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub +org.apache.qpid.test.client.FlowControlTest#* +org.apache.qpid.test.unit.client.connection.ConnectionTest#testDefaultExchanges +org.apache.qpid.test.unit.client.connection.ConnectionTest#testUnresolvedVirtualHostFailure +// the 0.10 c++ broker does not implement forget +org.apache.qpid.test.unit.xa.FaultTest#testForget +// the 0-10 c++ broker does not implement priority / this test depends on a Java broker extension for queue creation +org.apache.qpid.server.queue.PriorityTest +//this test checks explicitly for 0-8 flow control semantics +org.apache.qpid.test.client.FlowControlTest +// 0-10 c++ broker doesn't implement virtual hosts, or those wackhy exchanges +org.apache.qpid.test.unit.client.connection.ConnectionTest#testUnresolvedVirtualHostFailure +org.apache.qpid.test.unit.client.connection.ConnectionTest#testDefaultExchanges +// 0-10 c++ broker in cpp.testprofile is started with no auth so won't pass this test +org.apache.qpid.test.unit.client.connection.ConnectionTest#testPasswordFailureConnection +// c++ broker doesn't do selectors, so this will fail +org.apache.qpid.test.unit.topic.TopicSessionTest#testNonMatchingMessagesDoNotFillQueue +// QPID-1225 : Temporary remove this test until the problem has been addressed +org.apache.qpid.server.security.acl.SimpleACLTest#testClientPublishInvalidQueueSuccess +// InVM Broker tests +org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* +// QPID-1262, QPID-1119 : This test fails occasionally due to potential protocol issue. +org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* diff --git a/RC6/qpid/java/010ExcludeList-noPrefetch b/RC6/qpid/java/010ExcludeList-noPrefetch new file mode 100644 index 0000000000..dca931b55b --- /dev/null +++ b/RC6/qpid/java/010ExcludeList-noPrefetch @@ -0,0 +1,61 @@ +org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* +org.apache.qpid.client.ResetMessageListenerTest#* +org.apache.qpid.test.unit.transacted.TransactedTest#testRollback +// those tests need durable subscribe states to be persisted +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testDurSubRestoredAfterNonPersistentMessageSent +org.apache.qpid.test.unit.ct.DurableSubscriberTest#testDurSubRestoresMessageSelector +// This test cannot be run with no-prefetch +org.apache.qpid.test.unit.xa.TopicTest#testMultiMessagesDurSubCrash +//These tests are for the java broker +org.apache.qpid.server.security.acl.SimpleACLTest#* +org.apache.qpid.server.plugins.PluginTest#* +// This test is not finished +org.apache.qpid.test.testcases.TTLTest#* +// Those tests require failover support +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverWithQueueBrowser +org.apache.qpid.test.testcases.FailoverTest#* +org.apache.qpid.test.client.failover.FailoverTest#* +// Those tests are testing 0.8 specific semantics +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub +org.apache.qpid.test.client.FlowControlTest#* +org.apache.qpid.test.unit.client.connection.ConnectionTest#testDefaultExchanges +org.apache.qpid.test.unit.client.connection.ConnectionTest#testUnresolvedVirtualHostFailure +// the 0.10 c++ broker does not implement forget +org.apache.qpid.test.unit.xa.FaultTest#testForget +// the 0-10 c++ broker does not implement priority / this test depends on a Java broker extension for queue creation +org.apache.qpid.server.queue.PriorityTest +//this test checks explicitly for 0-8 flow control semantics +org.apache.qpid.test.client.FlowControlTest +// The default cpp.testprofile does not start the cpp broker with authentication so this test will fail. +org.apache.qpid.test.unit.client.connection.ConnectionTest#testPasswordFailureConnection +// c++ broker doesn't do selectors, so this will fail +org.apache.qpid.test.unit.topic.TopicSessionTest#testNonMatchingMessagesDoNotFillQueue +// QPID-1225 : Temporary remove this test until the problem has been addressed +org.apache.qpid.server.security.acl.SimpleACLTest#testClientPublishInvalidQueueSuccess + +// InVM Broker tests +org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* +// QPID-1262, QPID-1119 : This test fails occasionally due to potential protocol issue. +org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* diff --git a/RC6/qpid/java/010ExcludeList-store b/RC6/qpid/java/010ExcludeList-store new file mode 100644 index 0000000000..d4bc8b7b71 --- /dev/null +++ b/RC6/qpid/java/010ExcludeList-store @@ -0,0 +1,64 @@ +org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* +org.apache.qpid.client.ResetMessageListenerTest#* +// those tests should be run with prefetch off +org.apache.qpid.client.MessageListenerMultiConsumerTest#testRecieveC2Only +org.apache.qpid.client.MessageListenerMultiConsumerTest#testRecieveBoth +org.apache.qpid.test.unit.xa.TopicTest#testMultiMessagesDurSubCrash +org.apache.qpid.test.unit.xa.TopicTest#testMigrateDurableSubscriber +org.apache.qpid.test.unit.ack.AcknowledgeTest#* +// those tests need durable subscribe states to be persisted +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testDurSubRestoredAfterNonPersistentMessageSent +org.apache.qpid.test.unit.ct.DurableSubscriberTest#testDurSubRestoresMessageSelector +//These tests are for the java broker +org.apache.qpid.server.security.acl.SimpleACLTest#* +org.apache.qpid.server.plugins.PluginTest#* +// This test is not finished +org.apache.qpid.test.testcases.TTLTest#* +// Those tests require failover support +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverWithQueueBrowser +org.apache.qpid.test.testcases.FailoverTest#* +org.apache.qpid.test.client.failover.FailoverTest#* +// Those tests are testing 0.8 specific semantics +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxP2P +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.ImmediateMessageTest#test_QPID_517_ImmediateFailsNoRouteTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxP2P +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteNoTxPubSub +org.apache.qpid.test.testcases.MandatoryMessageTest#test_QPID_508_MandatoryFailsNoRouteTxPubSub +org.apache.qpid.test.client.FlowControlTest#* +org.apache.qpid.test.unit.client.connection.ConnectionTest#testDefaultExchanges +org.apache.qpid.test.unit.client.connection.ConnectionTest#testUnresolvedVirtualHostFailure +// the 0.10 c++ broker does not implement forget +org.apache.qpid.test.unit.xa.FaultTest#testForget +// the 0-10 c++ broker does not implement priority / this test depends on a Java broker extension for queue creation +org.apache.qpid.server.queue.PriorityTest +//this test checks explicitly for 0-8 flow control semantics +org.apache.qpid.test.client.FlowControlTest +// The default cpp.testprofile does not start the cpp broker with authentication so this test will fail. +org.apache.qpid.test.unit.client.connection.ConnectionTest#testPasswordFailureConnection +// c++ broker doesn't do selectors, so this will fail +org.apache.qpid.test.unit.topic.TopicSessionTest#testNonMatchingMessagesDoNotFillQueue +// QPID-1225 : Temporary remove this test until the problem has been addressed +org.apache.qpid.server.security.acl.SimpleACLTest#testClientPublishInvalidQueueSuccess + +// InVM Broker tests +org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* +// QPID-1262, QPID-1119 : This test fails occasionally due to potential protocol issue. +org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* diff --git a/RC6/qpid/java/08ExcludeList b/RC6/qpid/java/08ExcludeList new file mode 100644 index 0000000000..88eb754950 --- /dev/null +++ b/RC6/qpid/java/08ExcludeList @@ -0,0 +1,8 @@ +org.apache.qpid.test.unit.ct.DurableSubscriberTests#* +// Those tests are not finished +org.apache.qpid.test.testcases.TTLTest#* +org.apache.qpid.test.testcases.FailoverTest#* +// This is a long running test so should exclude from normal runs +org.apache.qpid.test.client.failover.FailoverTest#test4MinuteFailover +// Those tests are written against the 0.10 path +org.apache.qpid.test.unit.message.UTF8Test#* diff --git a/RC6/qpid/java/08ExcludeList-nonvm b/RC6/qpid/java/08ExcludeList-nonvm new file mode 100644 index 0000000000..eb6c60b225 --- /dev/null +++ b/RC6/qpid/java/08ExcludeList-nonvm @@ -0,0 +1,29 @@ +org.apache.qpid.test.unit.ct.DurableSubscriberTests#* +// Those tests are not finished +org.apache.qpid.test.testcases.TTLTest#* +org.apache.qpid.test.testcases.FailoverTest#* +// This is a long running test so should exclude from normal runs +org.apache.qpid.test.client.failover.FailoverTest#test4MinuteFailover +// Those tests require failover support +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserClientAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserNoAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserPreAckTest#testFailoverWithQueueBrowser +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverAsQueueBrowserCreated +org.apache.qpid.test.client.QueueBrowserTransactedTest#testFailoverWithQueueBrowser +org.apache.qpid.test.testcases.FailoverTest#* +org.apache.qpid.test.client.failover.FailoverTest#* + +// InVM Broker tests awaiting resolution of QPID-1103 +org.apache.qpid.test.client.timeouts.SyncWaitDelayTest#* +org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* +org.apache.qpid.server.security.acl.SimpleACLTest#* + +// Those tests are written against the 0.10 path +org.apache.qpid.test.unit.message.UTF8Test#* diff --git a/RC6/qpid/java/ExcludeList b/RC6/qpid/java/ExcludeList new file mode 100644 index 0000000000..509f74bbbd --- /dev/null +++ b/RC6/qpid/java/ExcludeList @@ -0,0 +1,8 @@ +org.apache.qpid.client.MultipleJCAProviderRegistrationTest#test +// QPID-1451 : testBrowsingWithSelector test is not correct. +org.apache.qpid.test.client.QueueBrowserAutoAckTest#testBrowsingWithSelector +org.apache.qpid.test.client.QueueBrowserClientAckTest#testBrowsingWithSelector +org.apache.qpid.test.client.QueueBrowserDupsOkTest#testBrowsingWithSelector +org.apache.qpid.test.client.QueueBrowserNoAckTest#testBrowsingWithSelector +org.apache.qpid.test.client.QueueBrowserPreAckTest#testBrowsingWithSelector +org.apache.qpid.test.client.QueueBrowserTransactedTest#testBrowsingWithSelector diff --git a/RC6/qpid/java/KEYS b/RC6/qpid/java/KEYS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/RC6/qpid/java/XAExcludeList b/RC6/qpid/java/XAExcludeList new file mode 100644 index 0000000000..1bb26c5f27 --- /dev/null +++ b/RC6/qpid/java/XAExcludeList @@ -0,0 +1,3 @@ +org.apache.qpid.test.unit.xa.QueueTest#* +org.apache.qpid.test.unit.xa.TopicTest#* +org.apache.qpid.test.unit.xa.FaultTest#* diff --git a/RC6/qpid/java/broker-plugins/MANIFEST.MF b/RC6/qpid/java/broker-plugins/MANIFEST.MF new file mode 100644 index 0000000000..e682614ed4 --- /dev/null +++ b/RC6/qpid/java/broker-plugins/MANIFEST.MF @@ -0,0 +1,14 @@ +Bundle-ManifestVersion: 2 +Bundle-Name: Qpid Plugins +Bundle-Description: A simple plugin for qpid. +Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt +Bundle-DocURL: http://www.apache.org/ +Bundle-SymbolicName: org.apache.qpid.extras.example-plugin +Bundle-Activator: org.apache.qpid.extras.Activator +Private-Package: org.apache.qpid.extras,org.apache.qpid.extras.exchang + es.diagnostic,org.apache.qpid.extras.exchanges.example +Import-Package: javax.management,javax.management.openmbean,org.apache + .qpid,org.apache.qpid.framing,org.apache.qpid.junit.extensions.util,o + rg.apache.qpid.protocol,org.apache.qpid.server.exchange,org.apache.qp + id.server.management,org.apache.qpid.server.queue,org.apache.qpid.ser + ver.virtualhost,org.osgi.framework;version=1.3 diff --git a/RC6/qpid/java/broker-plugins/build.xml b/RC6/qpid/java/broker-plugins/build.xml new file mode 100644 index 0000000000..9787eeebc3 --- /dev/null +++ b/RC6/qpid/java/broker-plugins/build.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/Activator.java b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/Activator.java new file mode 100644 index 0000000000..ca6c05a435 --- /dev/null +++ b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/Activator.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.extras; + +import org.apache.qpid.extras.exchanges.diagnostic.DiagnosticExchangeType; +import org.apache.qpid.extras.exchanges.example.TestExchangeType; +import org.apache.qpid.server.exchange.ExchangeType; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * + * @author aidan + * + * Dummy class, used by PluginTest + */ + +public class Activator implements BundleActivator +{ + + public void start(BundleContext ctx) throws Exception + { + ctx.registerService(ExchangeType.class.getName(), new TestExchangeType(), null); + ctx.registerService(ExchangeType.class.getName(), new DiagnosticExchangeType(), null); + } + + public void stop(BundleContext ctx) throws Exception + { + } +} diff --git a/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java new file mode 100644 index 0000000000..027d220538 --- /dev/null +++ b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java @@ -0,0 +1,219 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.extras.exchanges.diagnostic; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; + +import javax.management.JMException; +import javax.management.openmbean.OpenDataException; +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.FieldTable; +import org.apache.qpid.server.exchange.AbstractExchange; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; + +import org.apache.qpid.junit.extensions.util.SizeOf; + +/** + * + * This is a special diagnostic exchange type which doesn't actually do anything + * with messages. When it receives a message, it writes information about the + * current memory usage to the "memory" property of the message and places it on the + * diagnosticqueue for retrieval + * + * @author Aidan Skinner + * + */ + +public class DiagnosticExchange extends AbstractExchange +{ + + public static final AMQShortString DIAGNOSTIC_EXCHANGE_CLASS = new AMQShortString("x-diagnostic"); + public static final AMQShortString DIAGNOSTIC_EXCHANGE_NAME = new AMQShortString("diagnostic"); + + /** + * the logger. + */ + //private static final Logger _logger = Logger.getLogger(DiagnosticExchange.class); + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Diagnostic Exchange") + private final class DiagnosticExchangeMBean extends ExchangeMBean + { + + /** + * Usual constructor. + * + * @throws JMException + */ + @MBeanConstructor("Creates an MBean for AMQ Diagnostic exchange") + public DiagnosticExchangeMBean() throws JMException + { + super(); + _exchangeType = "diagnostic"; + init(); + } + + /** + * Returns nothing, there can be no tabular data for this... + * + * @throws OpenDataException + * @returns null + * @todo ... or can there? Could this actually return all the + * information in one easy to read table? + */ + public TabularData bindings() throws OpenDataException + { + return null; + } + + /** + * This exchange type doesn't support queues, so this method does + * nothing. + * + * @param queueName + * the queue you'll fail to create + * @param binding + * the binding you'll fail to create + * @throws JMException + * an exception that will never be thrown + */ + public void createNewBinding(String queueName, String binding) throws JMException + { + // No Op + } + + } // End of MBean class + + /** + * Creates a new MBean instance + * + * @return the newly created MBean + * @throws AMQException + * if something goes wrong + */ + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new DiagnosticExchange.DiagnosticExchangeMBean(); + } + catch (JMException ex) + { + // _logger.error("Exception occured in creating the direct exchange mbean", ex); + throw new AMQException(null, "Exception occured in creating the direct exchange mbean", ex); + } + } + + public AMQShortString getType() + { + return DIAGNOSTIC_EXCHANGE_CLASS; + } + + /** + * Does nothing. + * + * @param routingKey + * pointless + * @param queue + * pointless + * @param args + * pointless + * @throws AMQException + * never + */ + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + // No op + } + + /** + * Does nothing. + * + * @param routingKey + * pointless + * @param queue + * pointless + * @param args + * pointless + * @throws AMQException + * never + */ + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + // No op + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return false; + } + + public boolean isBound(AMQShortString routingKey) + { + return false; + } + + public boolean isBound(AMQQueue queue) + { + return false; + } + + public boolean hasBindings() + { + return false; + } + + public void route(IncomingMessage payload) throws AMQException + { + + Long value = new Long(SizeOf.getUsedMemory()); + AMQShortString key = new AMQShortString("memory"); + + FieldTable headers = ((BasicContentHeaderProperties)payload.getContentHeaderBody().properties).getHeaders(); + headers.put(key, value); + ((BasicContentHeaderProperties)payload.getContentHeaderBody().properties).setHeaders(headers); + AMQQueue q = getQueueRegistry().getQueue(new AMQShortString("diagnosticqueue")); + + ArrayList queues = new ArrayList(); + queues.add(q); + payload.enqueue(queues); + + } + + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, + AMQQueue queue) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.java new file mode 100644 index 0000000000..d96b4dc99e --- /dev/null +++ b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchangeType.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.extras.exchanges.diagnostic; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * Exchange type class for getting hold of the exchange. + */ +public final class DiagnosticExchangeType implements ExchangeType +{ + + public AMQShortString getName() + { + return DiagnosticExchange.DIAGNOSTIC_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return DiagnosticExchange.class; + } + + public DiagnosticExchange newInstance(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + throws AMQException + { + DiagnosticExchange exch = new DiagnosticExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return DiagnosticExchange.DIAGNOSTIC_EXCHANGE_NAME; + } +} diff --git a/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java new file mode 100644 index 0000000000..e43bd2ddc0 --- /dev/null +++ b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java @@ -0,0 +1,118 @@ +package org.apache.qpid.extras.exchanges.example; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.util.List; +import java.util.Map; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class TestExchange implements Exchange +{ + + public void close() throws AMQException + { + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + } + + public Map> getBindings() + { + return null; + } + + public AMQShortString getName() + { + return null; + } + + public AMQShortString getType() + { + return null; + } + + public boolean hasBindings() + { + return false; + } + + public void initialise(VirtualHost host, AMQShortString name, boolean durable, boolean autoDelete) + throws AMQException + { + } + + public boolean isAutoDelete() + { + return false; + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return false; + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return false; + } + + public boolean isBound(AMQShortString routingKey) + { + return false; + } + + public boolean isBound(AMQQueue queue) + { + return false; + } + + public boolean isDurable() + { + return false; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + } + + public void route(IncomingMessage message) throws AMQException + { + } + + public int getTicket() + { + return 0; + } + + public void initialise(VirtualHost arg0, AMQShortString arg1, boolean arg2, int arg3, boolean arg4) + throws AMQException + { + } +} diff --git a/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.java new file mode 100644 index 0000000000..22833693ca --- /dev/null +++ b/RC6/qpid/java/broker-plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchangeType.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.extras.exchanges.example; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class TestExchangeType implements ExchangeType +{ + + public Class getExchangeClass() + { + return TestExchange.class; + } + + public AMQShortString getName() + { + return null; + } + + public Exchange newInstance(VirtualHost host, AMQShortString name, boolean durable, + int token, boolean autoDelete) + throws AMQException + { + TestExchange ex = new TestExchange(); + ex.initialise(host, name, durable, token, autoDelete); + return ex; + } + + public AMQShortString getDefaultExchangeName() + { + return new AMQShortString("test.exchange"); + } + +} diff --git a/RC6/qpid/java/broker/bin/msTool.sh b/RC6/qpid/java/broker/bin/msTool.sh new file mode 100755 index 0000000000..e190a0a46a --- /dev/null +++ b/RC6/qpid/java/broker/bin/msTool.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +die() { + if [[ $1 = -usage ]]; then + shift + usage=true + else + usage=false + fi + echo "$@" + $usage && echo + $usage && usage + exit 1 +} + +cygwin=false +if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then + cygwin=true +fi + +if [ -z "$QPID_TOOLS" ]; then + if [ -z "$QPID_HOME" ]; then + die "QPID_TOOLS must be set" + else + QPID_TOOLS=$QPID_HOME + fi +fi + +if $cygwin; then + QPID_TOOLS=$(cygpath -w $QPID_TOOLS) +fi + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_TOOLS/lib/qpid-all.jar + +# Set other variables used by the qpid-run script before calling +export JAVA=java \ + JAVA_VM=-server \ + JAVA_OPTS=-Dlog4j.configuration=file:$QPID_TOOLS/etc/mstool-log4j.xml \ + QPID_CLASSPATH=$QPID_LIBS + +. qpid-run org.apache.qpid.tools.messagestore.MessageStoreTool "$@" diff --git a/RC6/qpid/java/broker/bin/qpid-passwd b/RC6/qpid/java/broker/bin/qpid-passwd new file mode 100755 index 0000000000..63b30b5e71 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid-passwd @@ -0,0 +1,35 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$QPID_HOME" ]; then + export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) + export PATH=${PATH}:${QPID_HOME}/bin +fi + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar + +# Set other variables used by the qpid-run script before calling +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + QPID_CLASSPATH=$QPID_LIBS + +. qpid-run org.apache.qpid.tools.security.Passwd "$@" diff --git a/RC6/qpid/java/broker/bin/qpid-server b/RC6/qpid/java/broker/bin/qpid-server new file mode 100755 index 0000000000..3570056348 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid-server @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +if [ -z "$QPID_HOME" ]; then + export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) + export PATH=${PATH}:${QPID_HOME}/bin +fi + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar + +# Set other variables used by the qpid-run script before calling +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + JAVA_GC="-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError" \ + QPID_CLASSPATH=$QPID_LIBS \ + QPID_RUN_LOG=2 + +. qpid-run org.apache.qpid.server.Main "$@" diff --git a/RC6/qpid/java/broker/bin/qpid-server-bdb.bat b/RC6/qpid/java/broker/bin/qpid-server-bdb.bat new file mode 100755 index 0000000000..8964e577df --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid-server-bdb.bat @@ -0,0 +1,22 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +set BDBSTORE_HOME=c:\qpid\trunk\java\bdbstore +set QPID_MODULE_JARS=%BDBSTORE_HOME%\target\qpid-bdbstore-1.0-incubating-M2-SNAPSHOT.jar;%BDBSTORE_HOME%\lib\bdb\je-3.1.0.jar +.\qpid-server.bat \ No newline at end of file diff --git a/RC6/qpid/java/broker/bin/qpid-server.bat b/RC6/qpid/java/broker/bin/qpid-server.bat new file mode 100755 index 0000000000..2687baa111 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid-server.bat @@ -0,0 +1,203 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +REM set QPID_WORK if not set +if not "%QPID_WORK%" == "" goto okQpidWork +if "%HOME%" == "" goto noHome +set QPID_WOKR=%HOME% +goto okQpidWork + +:noHome +set QPID_WORK=c:\Temp +if not exist %QPID_WORK% md %QPID_WORK% +:okQpidWork + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto end +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto end +:okJavaHome + +REM set loggin level if not set +if "%AMQJ_LOGGING_LEVEL%" == "" set AMQJ_LOGGING_LEVEL=info + +REM Set the default system properties that we'll use now that they have +REM all been initialised +set SYSTEM_PROPS=-Damqj.logging.level=%AMQJ_LOGGING_LEVEL% -DQPID_HOME=%QPID_HOME% -DQPID_WORK=%QPID_WORK% + +if "%EXTERNAL_CLASSPATH%" == "" set EXTERNAL_CLASSPATH=%CLASSPATH% + +REM Use QPID_CLASSPATH if set +if "%QPID_CLASSPATH%" == "" goto noQpidClasspath +set CLASSPATH=%QPID_CLASSPATH% +echo Using CLASSPATH: %CLASSPATH% +goto afterQpidClasspath + +:noQpidClasspath +echo Warning: Qpid classpath not set. CLASSPATH set to %QPID_HOME%\lib\qpid-all.jar +set CLASSPATH=%QPID_HOME%\lib\qpid-all.jar +:afterQpidClasspath + +REM start parsing -run arguments +set QPID_ARGS= +if "%1" == "" goto endRunArgs +:runLoop +set var=%1 +if "%var:~0,5%" == "-run:" goto runFound +set QPID_ARGS=%QPID_ARGS% %1 +:beforeRunShift +shift +if not "%1"=="" goto runLoop +goto endRunArgs + +:runFound +if "%var%" == "-run:debug" goto runDebug +if "%var%" == "-run:jpda" goto runJdpa +if "%var:~0,24%" == "-run:external-classpath-" goto runExternalClasspath +if "%var%" == "-run:print-classpath" goto runPrintCP +if "%var%" == "-run:help" goto runHelp +echo "unrecognized -run option '%var%'. For using external classpaths use -run:external-classpath-option" +goto end + +:runDebug +REM USAGE: print the classpath and command before running it +set debug=true +goto beforeRunShift + +:runJdpa +REM USAGE: adds debugging options to the java command, use +REM USAGE: JDPA_TRANSPORT and JPDA_ADDRESS to customize the debugging +REM USAGE: behavior and use JPDA_OPTS to override it entirely +if "%JPDA_OPTS%" == "" goto beforeRunShift +if "%JPDA_TRANSPORT%" == "" set JPDA_TRANSPORT=-dt_socket +if "%JPDA_ADDRESS%" == "" set JPDA_ADDRESS=8000 +set JPDA_OPTS="-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=n" +set QPID_OPTS="%QPID_OPTS% %JPDA_OPTS%" +goto beforeRunShift + +:runExternalClasspath +echo Using external classpath %var% +REM USAGE: Format is -run:external-classpath-first/last/ignore/only as equals special in DOS +REM USAGE: controls how the CLASSPATH environment variable is used by +REM USAGE: this script, value can be one of ignore (the default), first, +REM USAGE: last, and only +if "%var%" == "-run:external-classpath-ignore" goto beforeRunShift +if "%var%" == "-run:external-classpath-first" goto extCPFirst +if "%var%" == "-run:external-classpath-last" goto extCPLast +if "%var%" == "-run:external-classpath-only" goto extCPOnly +echo Invalid value provided for external classpath. +goto end + +:extCPFirst +set CLASSPATH=%EXTERNAL_CLASSPATH%;%CLASSPATH% +goto beforeRunShift + +:extCPLast +set CLASSPATH=%CLASSPATH%;%EXTERNAL_CLASSPATH% +goto beforeRunShift + +:extCPonly +set CLASSPATH=%EXTERNAL_CLASSPATH% +goto beforeRunShift + +:runPrintCP +REM USAGE: print the classpath +echo %CLASSPATH% +goto beforeRunShift + +:runHelp +REM USAGE: print this message +echo ------------------------------------------------------------------------------------------- +echo -run:option where option can be the following. +echo debug : Prints classpath and command before running it +echo jpda : Adds remote debugging info using JPDA_OPTS. Use JPDA_TRANSPORT and JPDA_ADDRESS to +echo customize, JPDA_OPTS to override +echo external-classpath : Valid values are: ignore, first, last and only. +echo print-classpath : Prints classpath before running command +echo help : Prints this message +echo -------------------------------------------------------------------------------------------- +goto end + +REM end parsing -run arguments +:endRunArgs + +set JAVA_VM=-server +set JAVA_MEM=-Xmx1024m +set JAVA_GC=-XX:+UseConcMarkSweepGC +rem removing the following vm arg from JAVA_GC as it is supported on ly in Java 1.6 +rem -XX:+HeapDumpOnOutOfMemoryError" + +REM Use QPID_JAVA_GC if set +if "%QPID_JAVA_GC%" == "" goto noQpidJavaGC +set JAVA_GC=%QPID_JAVA_GC% +echo Using QPID_JAVA_GC setting: %QPID_JAVA_GC% +goto afteQpidJavaGC + +:noQPidJavaGC +echo Info: QPID_JAVA_GC not set. Defaulting to JAVA_GC %JAVA_GC% +:afterQpidJavaGC + +REM Use QPID_JAVA_MEM if set +if "%QPID_JAVA_MEM%" == "" goto noQpidJavaMem +set JAVA_MEM=%QPID_JAVA_MEM% +echo Using QPID_JAVA_MEM setting: %QPID_JAVA_MEM% +goto afterQpidJavaMem + +:noQpidJavaMem +echo Info: QPID_JAVA_MEM not set. Defaulting to JAVA_MEM %JAVA_MEM% +:after QpidJavaMem + + +rem QPID_OPTS intended to hold any -D props for use +rem user must enclose any value for QPID_OPTS in double quotes +:runCommand +set MODULE_JARS=%QPID_MODULE_JARS% +set COMMAND="%JAVA_HOME%\bin\java" %JAVA_VM% %JAVA_MEM% %JAVA_GC% %QPID_OPTS% %SYSTEM_PROPS% -cp "%CLASSPATH%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS% + +if "%debug%" == "true" echo %CLASSPATH%;%LAUNCH_JAR%;%MODULE_JARS% +if "%debug%" == "true" echo %COMMAND% +%COMMAND% + +:end diff --git a/RC6/qpid/java/broker/bin/qpid.start b/RC6/qpid/java/broker/bin/qpid.start new file mode 100755 index 0000000000..78c34e70b4 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid.start @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +exec qpid-server -run:debug "$@" \ No newline at end of file diff --git a/RC6/qpid/java/broker/bin/qpid.stop b/RC6/qpid/java/broker/bin/qpid.stop new file mode 100755 index 0000000000..316f8dff46 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid.stop @@ -0,0 +1,178 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# qpid.stop Script +# +# Script checks for a given pid running DEFAULT_SEARCH and attempts to quit it +# + +MAX_ATTEMPTS=2 +SLEEP_DELAY=1 +DEFAULT_SEARCH="PNAME=QPBRKR" + +if [ -z "$QPID_STOP_SEARCH" ]; then + SEARCH=$DEFAULT_SEARCH; +else + SEARCH=$QPID_STOP_SEARCH; +fi + +# +# Forcably Quit the specified PID($1) +# +forceQuit() +{ +kill -9 $1 +} + +# +# Gracefully ask the PID($1) to quit +# +quit() +{ +kill $1 +} + +# +# grep for the session ID ($1) and return 0 for successful quit and 1 for process alive +# +lookup_pid() +{ +result=`ps -e | grep $1 | wc -l` +} + +# +# grep ps for all instances of $SEARCH for the current user and collect PIDs +# +lookup_all_pids() +{ +pids=`pgrep -f -U $USER $SEARCH` +result_all=`echo -n $pids | wc -w` +} + +# +# check that the PID passed in is for a Qpid broker owned by this user and alive +# +validate_pid() +{ +result=`pgrep -fl $SEARCH | grep $1 | wc -l` +} + +# +# Show the PS output for given set of pids +# +showPids() +{ +ps -o user,pid,args -p $pids +} + +# +# Sleep and then check then lookup the PID($1) to ensure it has quit +# +check() +{ +echo "Waiting $SLEEP_DELAY second for $1 to exit" +sleep $SLEEP_DELAY +lookup_pid $1 +} + +# +# Verify the PID($1) is available +# +verifyPid() +{ +validate_pid $1 +if [[ $[$result] == 1 ]] ; then + brokerspid=$1 +else + echo "Unable to locate Qpid Broker Process with PID $1. Check PID and try again." + exit -1 +fi +} + +# +# Stops all Qpid brokers for current user +# +qpid_stopall_brokers() +{ +for pid in $pids ; do + lookup_pid $pid; + brokerspid=$pid; + stop_broker $pid; +done +} + +# +# Stops Qpid broker with brokerspid id +# +stop_broker() +{ +# Attempt to quit the process MAX_ATTEMPTS Times +attempt=0 +while [[ $[$result] > 0 && $[$attempt] < $[$MAX_ATTEMPTS] ]] ; do + quit $brokerspid + check $brokerspid + attempt=$[$attempt + 1] +done + +# Check that it has quit +if [[ $[$result] == 0 ]] ; then + echo "Process quit" +else + + attempt=0 + # Now attempt to force quit the process + while [[ $[$result] > 0 && $[$attempt] < $[$MAX_ATTEMPTS] ]] ; do + forceQuit $brokerspid + check $brokerspid + attempt=$[$attempt + 1] + done + + # Output final status + if [[ $[$result] > 0 && $[$attempt] == $[$MAX_ATTEMPTS] ]] ; then + echo "Stopped trying to kill process: $brokerspid" + echo "Attempted to stop $attempt times" + else + echo "Done " + fi +fi + +} + +# +# Main Run +# + +# Check if we are killing all qpid pids or just one. +# Now uses local function qpid_stopall_brokers +if [[ $# == 0 ]] ; then + lookup_all_pids + if [[ $[$result_all] > 0 ]] ; then + echo "Killing All Qpid Brokers for user: '$USER'" + qpid_stopall_brokers + else + echo "No Qpid Brokers found running for user: " $USER + fi + exit $result +else + verifyPid $1 + stop_broker + exit $result +fi + diff --git a/RC6/qpid/java/broker/bin/qpid.stopall b/RC6/qpid/java/broker/bin/qpid.stopall new file mode 100755 index 0000000000..b0ad506629 --- /dev/null +++ b/RC6/qpid/java/broker/bin/qpid.stopall @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# qpid.stopall script +# +# Script attempts to stop all PROGRAMs running under the current user +# Utilises qpid.stop to perform the actual stopping +# + +qpid.stop $* diff --git a/RC6/qpid/java/broker/build.xml b/RC6/qpid/java/broker/build.xml new file mode 100644 index 0000000000..26dcde2918 --- /dev/null +++ b/RC6/qpid/java/broker/build.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/broker/etc/access b/RC6/qpid/java/broker/etc/access new file mode 100644 index 0000000000..58b7443fa9 --- /dev/null +++ b/RC6/qpid/java/broker/etc/access @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +guest:localhost(rw),test(rw) \ No newline at end of file diff --git a/RC6/qpid/java/broker/etc/acl.config.xml b/RC6/qpid/java/broker/etc/acl.config.xml new file mode 100644 index 0000000000..614ecf0a88 --- /dev/null +++ b/RC6/qpid/java/broker/etc/acl.config.xml @@ -0,0 +1,231 @@ + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + + false + + false + nio + 5672 + 8672 + 32768 + 32768 + + + false + 8999 + false + + + + false + false + 65535 + false + false + + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + + passwordFile + ${conf}/passwd + + + + + + + org.apache.qpid.server.security.access.plugins.DenyAll + + + + ${conf}/jmxremote.access + passwordfile + + + + + ${conf}/virtualhosts + + + test + + + org.apache.qpid.server.store.MemoryMessageStore + + + + amq.direct + + 4235264 + + 2117632 + + 600000 + + + + + + org.apache.qpid.server.security.access.plugins.SimpleXML + + + + + + + + amq.direct + + + + + example.RequestQueue + + client + + + + + + tmp_* + + server + + + + TempQueue* + + server + + + + + + + + + + + + + + + + + client + + + + + + + example.RequestQueue + + server + + + + + + + + + + + + + + + + amq.direct + + client + + + + + + + example.RequestQueue + + server + + + + + + + + + + + + + + + + development + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + localhost + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + + + 0 + 2.0 + + + ${conf}/virtualhosts.xml + + + diff --git a/RC6/qpid/java/broker/etc/config.xml b/RC6/qpid/java/broker/etc/config.xml new file mode 100644 index 0000000000..6a8c011e77 --- /dev/null +++ b/RC6/qpid/java/broker/etc/config.xml @@ -0,0 +1,133 @@ + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + + false + + false + + nio + 5672 + 8672 + 32768 + 32768 + + + false + 8999 + false + + + + false + false + 65535 + false + false + + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + + passwordFile + ${conf}/passwd + + + + + + + org.apache.qpid.server.security.access.plugins.AllowAll + + + false + + + ${conf}/jmxremote.access + passwordfile + + + + + ${conf}/virtualhosts + + + localhost + + + org.apache.qpid.server.store.MemoryMessageStore + + + + 20000 + + + + + + + development + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + test + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts.xml + + + diff --git a/RC6/qpid/java/broker/etc/debug.log4j.xml b/RC6/qpid/java/broker/etc/debug.log4j.xml new file mode 100644 index 0000000000..71f9502b75 --- /dev/null +++ b/RC6/qpid/java/broker/etc/debug.log4j.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/broker/etc/jmxremote.access b/RC6/qpid/java/broker/etc/jmxremote.access new file mode 100644 index 0000000000..1a51a6991b --- /dev/null +++ b/RC6/qpid/java/broker/etc/jmxremote.access @@ -0,0 +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. + +#Generated by JMX Console : Last edited by user:admin +#Tue Jun 12 16:46:39 BST 2007 +admin=admin +guest=readonly +user=readwrite diff --git a/RC6/qpid/java/broker/etc/log4j.xml b/RC6/qpid/java/broker/etc/log4j.xml new file mode 100644 index 0000000000..eff5d17588 --- /dev/null +++ b/RC6/qpid/java/broker/etc/log4j.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/broker/etc/md5passwd b/RC6/qpid/java/broker/etc/md5passwd new file mode 100644 index 0000000000..6a149919de --- /dev/null +++ b/RC6/qpid/java/broker/etc/md5passwd @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +guest:CE4DQ6BIb/BVMN9scFyLtA== +admin:ISMvKXpXpadDiUoOSoAfww== +user:aBzonUodYLhwSa8s9A10sA== diff --git a/RC6/qpid/java/broker/etc/mstool-log4j.xml b/RC6/qpid/java/broker/etc/mstool-log4j.xml new file mode 100644 index 0000000000..8c46010e2d --- /dev/null +++ b/RC6/qpid/java/broker/etc/mstool-log4j.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/broker/etc/passwd b/RC6/qpid/java/broker/etc/passwd new file mode 100644 index 0000000000..7aca438551 --- /dev/null +++ b/RC6/qpid/java/broker/etc/passwd @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +guest:guest +client:guest +server:guest + diff --git a/RC6/qpid/java/broker/etc/passwdVhost b/RC6/qpid/java/broker/etc/passwdVhost new file mode 100644 index 0000000000..48ce8299b6 --- /dev/null +++ b/RC6/qpid/java/broker/etc/passwdVhost @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +guest:guest:localhost,test diff --git a/RC6/qpid/java/broker/etc/persistent_config.xml b/RC6/qpid/java/broker/etc/persistent_config.xml new file mode 100644 index 0000000000..b7965a8af6 --- /dev/null +++ b/RC6/qpid/java/broker/etc/persistent_config.xml @@ -0,0 +1,115 @@ + + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + nio + 5672 + 8672 + 32768 + 32768 + + + false + 8999 + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + + passwordFile + ${conf}/passwd + + + + + + + org.apache.qpid.server.security.access.plugins.AllowAll + + + ${conf}/jmxremote.access + passwordfile + + + + + + localhost + + + org.apache.qpid.server.store.berkeleydb.BDBMessageStore + ${work}/bdbstore/localhost-store + + + + + + development + + + org.apache.qpid.server.store.berkeleydb.BDBMessageStore + ${work}/bdbstore/development-store + + + + + + test + + + org.apache.qpid.server.store.berkeleydb.BDBMessageStore + ${work}/bdbstore/test-store + + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts.xml + + + diff --git a/RC6/qpid/java/broker/etc/qpid-server.conf b/RC6/qpid/java/broker/etc/qpid-server.conf new file mode 100644 index 0000000000..8a16849b04 --- /dev/null +++ b/RC6/qpid/java/broker/etc/qpid-server.conf @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +QPID_LIBS=$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/bdbstore-launch.jar + +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + CLASSPATH=$QPID_LIBS diff --git a/RC6/qpid/java/broker/etc/qpid-server.conf.jpp b/RC6/qpid/java/broker/etc/qpid-server.conf.jpp new file mode 100644 index 0000000000..3ed2431ef3 --- /dev/null +++ b/RC6/qpid/java/broker/etc/qpid-server.conf.jpp @@ -0,0 +1,49 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +QPID_LIBS=$(build-classpath backport-util-concurrent \ + commons-beanutils \ + commons-beanutils-core \ + commons-cli \ + commons-codec \ + commons-collections \ + commons-configuration \ + commons-digester \ + commons-lang \ + commons-logging \ + commons-logging-api \ + dom4j \ + geronimo-jms-1.1-api \ + isorelax \ + jaxen \ + log4j \ + mina/core \ + mina/filter-ssl \ + mina/java5 \ + msv-msv \ + qpid-broker \ + qpid-client \ + qpid-common \ + relaxngDatatype \ + slf4j) + +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + CLASSPATH=$QPID_LIBS diff --git a/RC6/qpid/java/broker/etc/qpid.passwd b/RC6/qpid/java/broker/etc/qpid.passwd new file mode 100644 index 0000000000..dbfb9d1923 --- /dev/null +++ b/RC6/qpid/java/broker/etc/qpid.passwd @@ -0,0 +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. +# +guest:CE4DQ6BIb/BVMN9scFyLtA== +admin:ISMvKXpXpadDiUoOSoAfww== +user:CE4DQ6BIb/BVMN9scFyLtA== +server:CE4DQ6BIb/BVMN9scFyLtA== +client:CE4DQ6BIb/BVMN9scFyLtA== diff --git a/RC6/qpid/java/broker/etc/transient_config.xml b/RC6/qpid/java/broker/etc/transient_config.xml new file mode 100644 index 0000000000..1dd693f60f --- /dev/null +++ b/RC6/qpid/java/broker/etc/transient_config.xml @@ -0,0 +1,112 @@ + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + nio + 5672 + 8672 + 32768 + 32768 + + + false + 8999 + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase + + + passwordFile + ${conf}/passwd + + + + + + + org.apache.qpid.server.security.access.plugins.AllowAll + + + ${conf}/jmxremote.access + passwordfile + + + + + + localhost + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + development + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + test + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts.xml + + + diff --git a/RC6/qpid/java/broker/etc/virtualhosts.xml b/RC6/qpid/java/broker/etc/virtualhosts.xml new file mode 100644 index 0000000000..f62ec3f5d7 --- /dev/null +++ b/RC6/qpid/java/broker/etc/virtualhosts.xml @@ -0,0 +1,123 @@ + + + + test + + localhost + + + + direct + test.direct + true + + + topic + test.topic + + + + amq.direct + 4235264 + 2117632 + 600000 + + + queue + + + ping + + + test-queue + + test.direct + true + + + + test-ping + + test.direct + + + + + + + + + + development + + + 30000 + 5000 + + queue + + amq.direct + 4235264 + 2117632 + 600000 + + + + ping + + amq.direct + 4235264 + 2117632 + 600000 + + + + + + + test + + + 30000 + 5000 + + queue + + amq.direct + 4235264 + 2117632 + 600000 + + + + ping + + amq.direct + 4235264 + 2117632 + 600000 + + + + + + diff --git a/RC6/qpid/java/broker/python-test.xml b/RC6/qpid/java/broker/python-test.xml new file mode 100755 index 0000000000..5c263e3169 --- /dev/null +++ b/RC6/qpid/java/broker/python-test.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/broker/src/main/grammar/SelectorParser.jj b/RC6/qpid/java/broker/src/main/grammar/SelectorParser.jj new file mode 100644 index 0000000000..c9e01cd01f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/grammar/SelectorParser.jj @@ -0,0 +1,621 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + // + // Original File from r450141 of the Apache ActiveMQ project + // + +// ---------------------------------------------------------------------------- +// OPTIONS +// ---------------------------------------------------------------------------- +options { + STATIC = false; + UNICODE_INPUT = true; + + // some performance optimizations + OPTIMIZE_TOKEN_MANAGER = true; + ERROR_REPORTING = false; +} + +// ---------------------------------------------------------------------------- +// PARSER +// ---------------------------------------------------------------------------- + +PARSER_BEGIN(SelectorParser) +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter.jms.selector; + +import java.io.StringReader; +import java.util.ArrayList; + +import org.apache.qpid.AMQInvalidArgumentException; +import org.apache.qpid.server.filter.ArithmeticExpression; +import org.apache.qpid.server.filter.BooleanExpression; +import org.apache.qpid.server.filter.ComparisonExpression; +import org.apache.qpid.server.filter.ConstantExpression; +import org.apache.qpid.server.filter.Expression; +import org.apache.qpid.server.filter.LogicExpression; +import org.apache.qpid.server.filter.PropertyExpression; +import org.apache.qpid.server.filter.UnaryExpression; + +/** + * JMS Selector Parser generated by JavaCC + * + * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj + */ +public class SelectorParser { + + public SelectorParser() { + this(new StringReader("")); + } + + public BooleanExpression parse(String sql) throws AMQInvalidArgumentException { + this.ReInit(new StringReader(sql)); + + try { + return this.JmsSelector(); + } + catch (Throwable e) { + throw (AMQInvalidArgumentException)new AMQInvalidArgumentException(sql,e); + } + + } + + private BooleanExpression asBooleanExpression(Expression value) throws ParseException { + if (value instanceof BooleanExpression) { + return (BooleanExpression) value; + } + if (value instanceof PropertyExpression) { + return UnaryExpression.createBooleanCast( value ); + } + throw new ParseException("Expression will not result in a boolean value: " + value); + } + + +} + +PARSER_END(SelectorParser) + +// ---------------------------------------------------------------------------- +// Tokens +// ---------------------------------------------------------------------------- + +/* White Space */ +SPECIAL_TOKEN : +{ + " " | "\t" | "\n" | "\r" | "\f" +} + +/* Comments */ +SKIP: +{ + +} + +SKIP: +{ + +} + +/* Reserved Words */ +TOKEN [IGNORE_CASE] : +{ + < NOT : "NOT"> + | < AND : "AND"> + | < OR : "OR"> + | < BETWEEN : "BETWEEN"> + | < LIKE : "LIKE"> + | < ESCAPE : "ESCAPE"> + | < IN : "IN"> + | < IS : "IS"> + | < TRUE : "TRUE" > + | < FALSE : "FALSE" > + | < NULL : "NULL" > + | < XPATH : "XPATH" > + | < XQUERY : "XQUERY" > +} + +/* Literals */ +TOKEN [IGNORE_CASE] : +{ + + < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? > + | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > + | < OCTAL_LITERAL: "0" (["0"-"7"])* > + | < FLOATING_POINT_LITERAL: + (["0"-"9"])+ "." (["0"-"9"])* ()? // matches: 5.5 or 5. or 5.5E10 or 5.E10 + | "." (["0"-"9"])+ ()? // matches: .5 or .5E10 + | (["0"-"9"])+ // matches: 5E10 + > + | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ > + | < STRING_LITERAL: "'" ( ("''") | ~["'"] )* "'" > +} + +TOKEN [IGNORE_CASE] : +{ + < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* > + | < QUOTED_ID : "\"" ( ("\"\"") | ~["\""] )* "\"" > +} + +// ---------------------------------------------------------------------------- +// Grammer +// ---------------------------------------------------------------------------- +BooleanExpression JmsSelector() : +{ + Expression left=null; +} +{ + ( + left = orExpression() + ) + { + return asBooleanExpression(left); + } + +} + +Expression orExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = andExpression() + ( + right = andExpression() + { + left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } + +} + + +Expression andExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = equalityExpression() + ( + right = equalityExpression() + { + left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } +} + +Expression equalityExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = comparisonExpression() + ( + + "=" right = comparisonExpression() + { + left = ComparisonExpression.createEqual(left, right); + } + | + "<>" right = comparisonExpression() + { + left = ComparisonExpression.createNotEqual(left, right); + } + | + LOOKAHEAD(2) + + { + left = ComparisonExpression.createIsNull(left); + } + | + + { + left = ComparisonExpression.createIsNotNull(left); + } + )* + ) + { + return left; + } +} + +Expression comparisonExpression() : +{ + Expression left; + Expression right; + Expression low; + Expression high; + String t, u; + boolean not; + ArrayList list; +} +{ + ( + left = addExpression() + ( + + ">" right = addExpression() + { + left = ComparisonExpression.createGreaterThan(left, right); + } + | + ">=" right = addExpression() + { + left = ComparisonExpression.createGreaterThanEqual(left, right); + } + | + "<" right = addExpression() + { + left = ComparisonExpression.createLessThan(left, right); + } + | + "<=" right = addExpression() + { + left = ComparisonExpression.createLessThanEqual(left, right); + } + | + { + u=null; + } + t = stringLitteral() + [ u = stringLitteral() ] + { + left = ComparisonExpression.createLike(left, t, u); + } + | + LOOKAHEAD(2) + { + u=null; + } + t = stringLitteral() [ u = stringLitteral() ] + { + left = ComparisonExpression.createNotLike(left, t, u); + } + | + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createBetween(left, low, high); + } + | + LOOKAHEAD(2) + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createNotBetween(left, low, high); + } + | + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createInFilter(left, list); + } + | + LOOKAHEAD(2) + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createNotInFilter(left, list); + } + + )* + ) + { + return left; + } +} + +Expression addExpression() : +{ + Expression left; + Expression right; +} +{ + left = multExpr() + ( + LOOKAHEAD( ("+"|"-") multExpr()) + ( + "+" right = multExpr() + { + left = ArithmeticExpression.createPlus(left, right); + } + | + "-" right = multExpr() + { + left = ArithmeticExpression.createMinus(left, right); + } + ) + + )* + { + return left; + } +} + +Expression multExpr() : +{ + Expression left; + Expression right; +} +{ + left = unaryExpr() + ( + "*" right = unaryExpr() + { + left = ArithmeticExpression.createMultiply(left, right); + } + | + "/" right = unaryExpr() + { + left = ArithmeticExpression.createDivide(left, right); + } + | + "%" right = unaryExpr() + { + left = ArithmeticExpression.createMod(left, right); + } + + )* + { + return left; + } +} + + +Expression unaryExpr() : +{ + String s=null; + Expression left=null; +} +{ + ( + LOOKAHEAD( "+" unaryExpr() ) + "+" left=unaryExpr() + | + "-" left=unaryExpr() + { + left = UnaryExpression.createNegate(left); + } + | + left=unaryExpr() + { + left = UnaryExpression.createNOT( asBooleanExpression(left) ); + } + | + s=stringLitteral() + { + left = UnaryExpression.createXPath( s ); + } + | + s=stringLitteral() + { + left = UnaryExpression.createXQuery( s ); + } + | + left = primaryExpr() + ) + { + return left; + } + +} + +Expression primaryExpr() : +{ + Expression left=null; +} +{ + ( + left = literal() + | + left = variable() + | + "(" left = orExpression() ")" + ) + { + return left; + } +} + + + +ConstantExpression literal() : +{ + Token t; + String s; + ConstantExpression left=null; +} +{ + ( + ( + s = stringLitteral() + { + left = new ConstantExpression(s); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromDecimal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromHex(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromOctal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFloat(t.image); + } + ) + | + ( + + { + left = ConstantExpression.TRUE; + } + ) + | + ( + + { + left = ConstantExpression.FALSE; + } + ) + | + ( + + { + left = ConstantExpression.NULL; + } + ) + ) + { + return left; + } +} + +String stringLitteral() : +{ + Token t; + StringBuffer rc = new StringBuffer(); + boolean first=true; +} +{ + t = + { + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '\'' ) + i++; + rc.append(c); + } + return rc.toString(); + } +} + +PropertyExpression variable() : +{ + Token t; + StringBuffer rc = new StringBuffer(); + PropertyExpression left=null; +} +{ + ( + t = + { + left = new PropertyExpression(t.image); + } + | + t = + { + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '"' ) + i++; + rc.append(c); + } + return new PropertyExpression(rc.toString()); + } + + + ) + { + return left; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/log4j.properties b/RC6/qpid/java/broker/src/main/java/log4j.properties new file mode 100644 index 0000000000..6788c65463 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/log4j.properties @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +log4j.rootCategory=${amqj.logging.level}, console + +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.Threshold=all +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java b/RC6/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java new file mode 100644 index 0000000000..7e0c4defe1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java @@ -0,0 +1,1007 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 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; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPOutputStream; + +import org.apache.log4j.helpers.CountingQuietWriter; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.helpers.OptionConverter; +import org.apache.log4j.spi.LoggingEvent; + +/** + *

CompositeRollingAppender combines RollingFileAppender and DailyRollingFileAppender
It can function as either + * or do both at the same time (making size based rolling files like RollingFileAppender until a data/time boundary is + * crossed at which time it rolls all of those files as per the DailyRollingFileAppender) based on the setting for + * rollingStyle.

To use CompositeRollingAppender to roll log files as they reach a certain size + * (like RollingFileAppender), set rollingStyle=1 (@see config.size)
To use CompositeRollingAppender to roll log + * files at certain time intervals (daily for example), set rollingStyle=2 and a datePattern (@see config.time)
To + * have CompositeRollingAppender roll log files at a certain size AND rename those according to time intervals, set + * rollingStyle=3 (@see config.composite)
+ * + *

A of few additional optional features have been added:
-- Attach date pattern for current log file (@see + * staticLogFileName)
-- Backup number increments for newer files (@see countDirection)
-- Infinite number of + * backups by file size (@see maxSizeRollBackups)

A few notes and warnings: For large or infinite number of + * backups countDirection > 0 is highly recommended, with staticLogFileName = false if time based rolling is also used + * -- this will reduce the number of file renamings to few or none. Changing staticLogFileName or countDirection + * without clearing the directory could have nasty side effects. If Date/Time based rolling is enabled, + * CompositeRollingAppender will attempt to roll existing files in the directory without a date/time tag based on the + * last modified date of the base log files last modification.

A maximum number of backups based on + * date/time boundries would be nice but is not yet implemented.
+ * + * @author Kevin Steppe + * @author Heinz Richter + * @author Eirik Lygre + * @author Ceki Gülcü + * @author Martin Ritchie + */ +public class QpidCompositeRollingAppender extends FileAppender +{ + // The code assumes that the following 'time' constants are in a increasing + // sequence. + static final int TOP_OF_TROUBLE = -1; + static final int TOP_OF_MINUTE = 0; + static final int TOP_OF_HOUR = 1; + static final int HALF_DAY = 2; + static final int TOP_OF_DAY = 3; + static final int TOP_OF_WEEK = 4; + static final int TOP_OF_MONTH = 5; + + /** Style of rolling to use */ + static final int BY_SIZE = 1; + static final int BY_DATE = 2; + static final int BY_COMPOSITE = 3; + + // Not currently used + static final String S_BY_SIZE = "Size"; + static final String S_BY_DATE = "Date"; + static final String S_BY_COMPOSITE = "Composite"; + + /** The date pattern. By default, the pattern is set to "'.'yyyy-MM-dd" meaning daily rollover. */ + private String datePattern = "'.'yyyy-MM-dd"; + + /** + * The actual formatted filename that is currently being written to or will be the file transferred to on roll over + * (based on staticLogFileName). + */ + private String scheduledFilename = null; + + /** The timestamp when we shall next recompute the filename. */ + private long nextCheck = System.currentTimeMillis() - 1; + + /** Holds date of last roll over */ + Date now = new Date(); + + SimpleDateFormat sdf; + + /** Helper class to determine next rollover time */ + RollingCalendar rc = new RollingCalendar(); + + /** Current period for roll overs */ + int checkPeriod = TOP_OF_TROUBLE; + + /** The default maximum file size is 10MB. */ + protected long maxFileSize = 10 * 1024 * 1024; + + /** There is zero backup files by default. */ + protected int maxSizeRollBackups = 0; + /** How many sized based backups have been made so far */ + protected int curSizeRollBackups = 0; + + /** not yet implemented */ + protected int maxTimeRollBackups = -1; + protected int curTimeRollBackups = 0; + + /** + * By default newer files have lower numbers. (countDirection < 0) ie. log.1 is most recent, log.5 is the 5th + * backup, etc... countDirection > 0 does the opposite ie. log.1 is the first backup made, log.5 is the 5th backup + * made, etc. For infinite backups use countDirection > 0 to reduce rollOver costs. + */ + protected int countDirection = -1; + + /** Style of rolling to Use. BY_SIZE (1), BY_DATE(2), BY COMPOSITE(3) */ + protected int rollingStyle = BY_COMPOSITE; + protected boolean rollDate = true; + protected boolean rollSize = true; + + /** + * By default file.log is always the current file. Optionally file.log.yyyy-mm-dd for current formated datePattern + * can by the currently logging file (or file.log.curSizeRollBackup or even file.log.yyyy-mm-dd.curSizeRollBackup) + * This will make time based roll overs with a large number of backups much faster -- it won't have to rename all + * the backups! + */ + protected boolean staticLogFileName = true; + + /** FileName provided in configuration. Used for rolling properly */ + protected String baseFileName; + + /** Do we want to .gz our backup files. */ + protected boolean compress = false; + + /** Do we want to use a second thread when compressing our backup files. */ + protected boolean compressAsync = false; + + /** Do we want to start numbering files at zero. */ + protected boolean zeroBased = false; + + /** Path provided in configuration. Used for moving backup files to */ + protected String backupFilesToPath = null; + private final ConcurrentLinkedQueue _compress = new ConcurrentLinkedQueue(); + private AtomicBoolean _compressing = new AtomicBoolean(false); + + /** The default constructor does nothing. */ + public QpidCompositeRollingAppender() + { } + + /** + * Instantiate a CompositeRollingAppender and open the file designated by filename. The + * opened filename will become the ouput destination for this appender. + */ + public QpidCompositeRollingAppender(Layout layout, String filename, String datePattern) throws IOException + { + this(layout, filename, datePattern, true); + } + + /** + * Instantiate a CompositeRollingAppender and open the file designated by filename. The opened filename + * will become the ouput destination for this appender. + * + *

If the append parameter is true, the file will be appended to. Otherwise, the file desginated by + * filename will be truncated before being opened. + */ + public QpidCompositeRollingAppender(Layout layout, String filename, boolean append) throws IOException + { + super(layout, filename, append); + } + + /** + * Instantiate a CompositeRollingAppender and open the file designated by filename. The opened filename + * will become the ouput destination for this appender. + */ + public QpidCompositeRollingAppender(Layout layout, String filename, String datePattern, boolean append) + throws IOException + { + super(layout, filename, append); + this.datePattern = datePattern; + activateOptions(); + } + + /** + * Instantiate a CompositeRollingAppender and open the file designated by filename. The opened filename + * will become the output destination for this appender. + * + *

The file will be appended to. DatePattern is default. + */ + public QpidCompositeRollingAppender(Layout layout, String filename) throws IOException + { + super(layout, filename); + } + + /** + * The DatePattern takes a string in the same format as expected by {@link java.text.SimpleDateFormat}. This + * options determines the rollover schedule. + */ + public void setDatePattern(String pattern) + { + datePattern = pattern; + } + + /** Returns the value of the DatePattern option. */ + public String getDatePattern() + { + return datePattern; + } + + /** Returns the value of the maxSizeRollBackups option. */ + public int getMaxSizeRollBackups() + { + return maxSizeRollBackups; + } + + /** + * Get the maximum size that the output file is allowed to reach before being rolled over to backup files. + * + * @since 1.1 + */ + public long getMaximumFileSize() + { + return maxFileSize; + } + + /** + *

Set the maximum number of backup files to keep around based on file size. + * + *

The MaxSizeRollBackups option determines how many backup files are kept before the oldest is erased. + * This option takes an integer value. If set to zero, then there will be no backup files and the log file will be + * truncated when it reaches MaxFileSize. If a negative number is supplied then no deletions will be + * made. Note that this could result in very slow performance as a large number of files are rolled over unless + * {@link #setCountDirection} up is used. + * + *

The maximum applys to -each- time based group of files and -not- the total. Using a daily roll the maximum + * total files would be (#days run) * (maxSizeRollBackups) + */ + public void setMaxSizeRollBackups(int maxBackups) + { + maxSizeRollBackups = maxBackups; + } + + /** + * Set the maximum size that the output file is allowed to reach before being rolled over to backup files. + * + *

This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter + * taking a long argument from the setter taking a String argument by the JavaBeans {@link + * java.beans.Introspector Introspector}. + * + * @see #setMaxFileSize(String) + */ + public void setMaxFileSize(long maxFileSize) + { + this.maxFileSize = maxFileSize; + } + + /** + * Set the maximum size that the output file is allowed to reach before being rolled over to backup files. + * + *

This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter + * taking a long argument from the setter taking a String argument by the JavaBeans {@link + * java.beans.Introspector Introspector}. + * + * @see #setMaxFileSize(String) + */ + public void setMaximumFileSize(long maxFileSize) + { + this.maxFileSize = maxFileSize; + } + + /** + * Set the maximum size that the output file is allowed to reach before being rolled over to backup files. + * + *

In configuration files, the MaxFileSize option takes an long integer in the range 0 - 2^63. You can + * specify the value with the suffixes "KB", "MB" or "GB" so that the integer is interpreted being expressed + * respectively in kilobytes, megabytes or gigabytes. For example, the value "10KB" will be interpreted as 10240. + */ + public void setMaxFileSize(String value) + { + maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); + } + + protected void setQWForFiles(Writer writer) + { + qw = new CountingQuietWriter(writer, errorHandler); + } + + // Taken verbatum from DailyRollingFileAppender + int computeCheckPeriod() + { + RollingCalendar c = new RollingCalendar(); + // set sate to 1970-01-01 00:00:00 GMT + Date epoch = new Date(0); + if (datePattern != null) + { + for (int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) + { + String r0 = sdf.format(epoch); + c.setType(i); + Date next = new Date(c.getNextCheckMillis(epoch)); + String r1 = sdf.format(next); + // LogLog.debug("Type = "+i+", r0 = "+r0+", r1 = "+r1); + if ((r0 != null) && (r1 != null) && !r0.equals(r1)) + { + return i; + } + } + } + + return TOP_OF_TROUBLE; // Deliberately head for trouble... + } + + // Now for the new stuff + /** + * Handles append time behavior for CompositeRollingAppender. This checks if a roll over either by date (checked + * first) or time (checked second) is need and then appends to the file last. + */ + protected void subAppend(LoggingEvent event) + { + + if (rollDate) + { + long n = System.currentTimeMillis(); + if (n >= nextCheck) + { + now.setTime(n); + nextCheck = rc.getNextCheckMillis(now); + + rollOverTime(); + } + } + + if (rollSize) + { + if ((fileName != null) && (((CountingQuietWriter) qw).getCount() >= maxFileSize)) + { + rollOverSize(); + } + } + + super.subAppend(event); + } + + public void setFile(String file) + { + baseFileName = file.trim(); + fileName = file.trim(); + } + + /** + * Creates and opens the file for logging. If staticLogFileName is false then the fully qualified name + * is determined and used. + */ + public synchronized void setFile(String fileName, boolean append) throws IOException + { + if (!staticLogFileName) + { + scheduledFilename = fileName = fileName.trim() + sdf.format(now); + if (countDirection > 0) + { + scheduledFilename = fileName = fileName + '.' + (++curSizeRollBackups); + } + } + + super.setFile(fileName, append, bufferedIO, bufferSize); + + if (append) + { + File f = new File(fileName); + ((CountingQuietWriter) qw).setCount(f.length()); + } + } + + public int getCountDirection() + { + return countDirection; + } + + public void setCountDirection(int direction) + { + countDirection = direction; + } + + public int getRollingStyle() + { + return rollingStyle; + } + + public void setRollingStyle(int style) + { + rollingStyle = style; + switch (rollingStyle) + { + + case BY_SIZE: + rollDate = false; + rollSize = true; + break; + + case BY_DATE: + rollDate = true; + rollSize = false; + break; + + case BY_COMPOSITE: + rollDate = true; + rollSize = true; + break; + + default: + errorHandler.error("Invalid rolling Style, use 1 (by size only), 2 (by date only) or 3 (both)"); + } + } + + /* + public void setRollingStyle(String style) { + if (style == S_BY_SIZE) { + rollingStyle = BY_SIZE; + } + else if (style == S_BY_DATE) { + rollingStyle = BY_DATE; + } + else if (style == S_BY_COMPOSITE) { + rollingStyle = BY_COMPOSITE; + } + } + */ + public boolean getStaticLogFileName() + { + return staticLogFileName; + } + + public void setStaticLogFileName(boolean s) + { + staticLogFileName = s; + } + + public void setStaticLogFileName(String value) + { + setStaticLogFileName(OptionConverter.toBoolean(value, true)); + } + + public boolean getCompressBackupFiles() + { + return compress; + } + + public void setCompressBackupFiles(boolean c) + { + compress = c; + } + + public boolean getCompressAsync() + { + return compressAsync; + } + + public void setCompressAsync(boolean c) + { + compressAsync = c; + if (compressAsync) + { + executor = Executors.newFixedThreadPool(1); + + compressor = new Compressor(); + } + } + + public boolean getZeroBased() + { + return zeroBased; + } + + public void setZeroBased(boolean z) + { + zeroBased = z; + } + + public String getBackupFilesToPath() + { + return backupFilesToPath; + } + + public void setbackupFilesToPath(String path) + { + File td = new File(path); + if (!td.exists()) + { + td.mkdirs(); + } + + backupFilesToPath = path; + } + + /** + * Initializes based on exisiting conditions at time of activateOptions. The following is done:
+ *
A) determine curSizeRollBackups
B) determine curTimeRollBackups (not implemented)
C) initiates a + * roll over if needed for crossing a date boundary since the last run. + */ + protected void existingInit() + { + + if (zeroBased) + { + curSizeRollBackups = -1; + } + + curTimeRollBackups = 0; + + // part A starts here + String filter; + if (staticLogFileName || !rollDate) + { + filter = baseFileName + ".*"; + } + else + { + filter = scheduledFilename + ".*"; + } + + File f = new File(baseFileName); + f = f.getParentFile(); + if (f == null) + { + f = new File("."); + } + + LogLog.debug("Searching for existing files in: " + f); + String[] files = f.list(); + + if (files != null) + { + for (int i = 0; i < files.length; i++) + { + if (!files[i].startsWith(baseFileName)) + { + continue; + } + + int index = files[i].lastIndexOf("."); + + if (staticLogFileName) + { + int endLength = files[i].length() - index; + if ((baseFileName.length() + endLength) != files[i].length()) + { + // file is probably scheduledFilename + .x so I don't care + continue; + } + } + + try + { + int backup = Integer.parseInt(files[i].substring(index + 1, files[i].length())); + LogLog.debug("From file: " + files[i] + " -> " + backup); + if (backup > curSizeRollBackups) + { + curSizeRollBackups = backup; + } + } + catch (Exception e) + { + // this happens when file.log -> file.log.yyyy-mm-dd which is normal + // when staticLogFileName == false + LogLog.debug("Encountered a backup file not ending in .x " + files[i]); + } + } + } + + LogLog.debug("curSizeRollBackups starts at: " + curSizeRollBackups); + // part A ends here + + // part B not yet implemented + + // part C + if (staticLogFileName && rollDate) + { + File old = new File(baseFileName); + if (old.exists()) + { + Date last = new Date(old.lastModified()); + if (!(sdf.format(last).equals(sdf.format(now)))) + { + scheduledFilename = baseFileName + sdf.format(last); + LogLog.debug("Initial roll over to: " + scheduledFilename); + rollOverTime(); + } + } + } + + LogLog.debug("curSizeRollBackups after rollOver at: " + curSizeRollBackups); + // part C ends here + + } + + /** + * Sets initial conditions including date/time roll over information, first check, scheduledFilename, and calls + * existingInit to initialize the current # of backups. + */ + public void activateOptions() + { + + // REMOVE removed rollDate from boolean to enable Alex's change + if (datePattern != null) + { + now.setTime(System.currentTimeMillis()); + sdf = new SimpleDateFormat(datePattern); + int type = computeCheckPeriod(); + // printPeriodicity(type); + rc.setType(type); + // next line added as this removes the name check in rollOver + nextCheck = rc.getNextCheckMillis(now); + } + else + { + if (rollDate) + { + LogLog.error("Either DatePattern or rollingStyle options are not set for [" + name + "]."); + } + } + + existingInit(); + + if (rollDate && (fileName != null) && (scheduledFilename == null)) + { + scheduledFilename = fileName + sdf.format(now); + } + + try + { + this.setFile(fileName, true); + } + catch (IOException e) + { + errorHandler.error("Cannot set file name:" + fileName); + } + + super.activateOptions(); + } + + /** + * Rollover the file(s) to date/time tagged file(s). Opens the new file (through setFile) and resets + * curSizeRollBackups. + */ + protected void rollOverTime() + { + + curTimeRollBackups++; + + this.closeFile(); // keep windows happy. + + // delete the old stuff here + + if (staticLogFileName) + { + /* Compute filename, but only if datePattern is specified */ + if (datePattern == null) + { + errorHandler.error("Missing DatePattern option in rollOver()."); + + return; + } + + // is the new file name equivalent to the 'current' one + // something has gone wrong if we hit this -- we should only + // roll over if the new file will be different from the old + String dateFormat = sdf.format(now); + if (scheduledFilename.equals(fileName + dateFormat)) + { + errorHandler.error("Compare " + scheduledFilename + " : " + fileName + dateFormat); + + return; + } + + // close current file, and rename it to datedFilename + this.closeFile(); + + // we may have to roll over a large number of backups here + String from, to; + for (int i = 1; i <= curSizeRollBackups; i++) + { + from = fileName + '.' + i; + to = scheduledFilename + '.' + i; + rollFile(from, to, false); + } + + rollFile(fileName, scheduledFilename, compress); + } + else + { + if (compress) + { + compress(fileName); + } + } + + try + { + // This will also close the file. This is OK since multiple + // close operations are safe. + curSizeRollBackups = 0; // We're cleared out the old date and are ready for the new + + // new scheduled name + scheduledFilename = fileName + sdf.format(now); + this.setFile(baseFileName, false); + } + catch (IOException e) + { + errorHandler.error("setFile(" + fileName + ", false) call failed."); + } + + } + + /** + * Renames file from to file to. It also checks for existence of target file and deletes + * if it does. + */ + protected void rollFile(String from, String to, boolean compress) + { + if (from.equals(to)) + { + if (compress) + { + LogLog.debug("Attempting to compress file with same output name."); + } + + return; + } + + File target = new File(to); + if (target.exists()) + { + LogLog.debug("deleting existing target file: " + target); + target.delete(); + } + + File file = new File(from); + if (compress) + { + compress(file, target); + } + else + { + if (!file.getPath().equals(target.getPath())) + { + file.renameTo(target); + } + } + + LogLog.debug(from + " -> " + to); + } + + protected void compress(String file) + { + File f = new File(file); + compress(f, f); + } + + private void compress(File from, File target) + { + if (compressAsync) + { + synchronized (_compress) + { + _compress.offer(new CompressJob(from, target)); + } + + startCompression(); + } + else + { + doCompress(from, target); + } + } + + private void startCompression() + { + if (_compressing.compareAndSet(false, true)) + { + executor.execute(compressor); + } + } + + /** Delete's the specified file if it exists */ + protected static void deleteFile(String fileName) + { + File file = new File(fileName); + if (file.exists()) + { + file.delete(); + } + } + + /** + * Implements roll overs base on file size. + * + *

If the maximum number of size based backups is reached (curSizeRollBackups == maxSizeRollBackups If + * countDirection < 0, then files {File.1, ..., File.curSizeRollBackups -1} + * are renamed to {File.2, ..., File.curSizeRollBackups}. Moreover, File is + * renamed File.1 and closed.
+ * + * A new file is created to receive further log output. + * + *

If maxSizeRollBackups is equal to zero, then the File is truncated with no backup + * files created. + * + *

If maxSizeRollBackups < 0, then File is renamed if needed and no files are deleted. + */ + + // synchronization not necessary since doAppend is alreasy synched + protected void rollOverSize() + { + File file; + + this.closeFile(); // keep windows happy. + + LogLog.debug("rolling over count=" + ((CountingQuietWriter) qw).getCount()); + LogLog.debug("maxSizeRollBackups = " + maxSizeRollBackups); + LogLog.debug("curSizeRollBackups = " + curSizeRollBackups); + LogLog.debug("countDirection = " + countDirection); + + // If maxBackups <= 0, then there is no file renaming to be done. + if (maxSizeRollBackups != 0) + { + + if (countDirection < 0) + { + // Delete the oldest file, to keep Windows happy. + if (curSizeRollBackups == maxSizeRollBackups) + { + deleteFile(fileName + '.' + maxSizeRollBackups); + curSizeRollBackups--; + } + + // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2} + for (int i = curSizeRollBackups; i >= 1; i--) + { + rollFile((fileName + "." + i), (fileName + '.' + (i + 1)), false); + } + + curSizeRollBackups++; + // Rename fileName to fileName.1 + rollFile(fileName, fileName + ".1", compress); + + } // REMOVE This code branching for Alexander Cerna's request + else if (countDirection == 0) + { + // rollFile based on date pattern + curSizeRollBackups++; + now.setTime(System.currentTimeMillis()); + scheduledFilename = fileName + sdf.format(now); + rollFile(fileName, scheduledFilename, compress); + } + else + { // countDirection > 0 + if ((curSizeRollBackups >= maxSizeRollBackups) && (maxSizeRollBackups > 0)) + { + // delete the first and keep counting up. + int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1; + deleteFile(fileName + '.' + oldestFileIndex); + } + + if (staticLogFileName) + { + curSizeRollBackups++; + rollFile(fileName, fileName + '.' + curSizeRollBackups, compress); + } + else + { + if (compress) + { + compress(fileName); + } + } + } + } + + try + { + // This will also close the file. This is OK since multiple + // close operations are safe. + this.setFile(baseFileName, false); + } + catch (IOException e) + { + LogLog.error("setFile(" + fileName + ", false) call failed.", e); + } + } + + protected synchronized void doCompress(File from, File to) + { + String toFile; + if (backupFilesToPath == null) + { + toFile = to.getPath() + ".gz"; + } + else + { + toFile = backupFilesToPath + System.getProperty("file.separator") + to.getName() + ".gz"; + } + + File target = new File(toFile); + if (target.exists()) + { + LogLog.debug("deleting existing target file: " + target); + target.delete(); + } + + try + { + // Create the GZIP output stream + GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(target)); + + // Open the input file + FileInputStream in = new FileInputStream(from); + + // Transfer bytes from the input file to the GZIP output stream + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) + { + out.write(buf, 0, len); + } + + in.close(); + + // Complete the GZIP file + out.finish(); + out.close(); + // Remove old file. + from.delete(); + } + catch (IOException e) + { + if (target.exists()) + { + target.delete(); + } + + rollFile(from.getPath(), to.getPath(), false); + } + } + + private class CompressJob + { + File _from, _to; + + CompressJob(File from, File to) + { + _from = from; + _to = to; + } + + File getFrom() + { + return _from; + } + + File getTo() + { + return _to; + } + } + + Compressor compressor = null; + + Executor executor; + + private class Compressor implements Runnable + { + public void run() + { + boolean running = true; + while (running) + { + CompressJob job = _compress.poll(); + + doCompress(job.getFrom(), job.getTo()); + + synchronized (_compress) + { + if (_compress.isEmpty()) + { + running = false; + _compressing.set(false); + } + } + } + + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java new file mode 100644 index 0000000000..40ff590a0a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.configuration; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class Configuration +{ + public static final String QPID_HOME = "QPID_HOME"; + + final String QPIDHOME = System.getProperty(QPID_HOME); + + private static Logger _devlog = LoggerFactory.getLogger(Configuration.class); + + public static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml"; + public static final String DEFAULT_CONFIG_FILE = "etc/config.xml"; + + protected final Options _options = new Options(); + protected CommandLine _commandLine; + protected File _configFile; + + + public Configuration() + { + + } + + public void processCommandline(String[] args) throws InitException + { + try + { + _commandLine = new PosixParser().parse(_options, args); + } + catch (ParseException e) + { + throw new InitException("Unable to parse commmandline", e); + } + + final File defaultConfigFile = new File(QPIDHOME, DEFAULT_CONFIG_FILE); + setConfig(new File(_commandLine.getOptionValue("c", defaultConfigFile.getPath()))); + } + + public void setConfig(File file) + { + _configFile = file; + } + + /** + * @param option The option to set. + */ + public void setOption(Option option) + { + _options.addOption(option); + } + + /** + * getOptionValue from the configuration + * @param option variable argument, first string is option to get, second if present is the default value. + * @return the String for the given option or null if not present (if default value not specified) + */ + public String getOptionValue(String... option) + { + if (option.length == 1) + { + return _commandLine.getOptionValue(option[0]); + } + else if (option.length == 2) + { + return _commandLine.getOptionValue(option[0], option[1]); + } + return null; + } + + public void loadConfig(File file) throws InitException + { + setConfig(file); + loadConfig(); + } + + private void loadConfig() throws InitException + { + if (!_configFile.exists()) + { + String error = "File " + _configFile + " could not be found. Check the file exists and is readable."; + + if (QPIDHOME == null) + { + error = error + "\nNote: " + QPID_HOME + " is not set."; + } + + throw new InitException(error, null); + } + else + { + _devlog.debug("Using configuration file " + _configFile.getAbsolutePath()); + } + +// String logConfig = _commandLine.getOptionValue("l"); +// String logWatchConfig = _commandLine.getOptionValue("w", "0"); +// if (logConfig != null) +// { +// File logConfigFile = new File(logConfig); +// configureLogging(logConfigFile, logWatchConfig); +// } +// else +// { +// File configFileDirectory = _configFile.getParentFile(); +// File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME); +// configureLogging(logConfigFile, logWatchConfig); +// } + } + + +// private void configureLogging(File logConfigFile, String logWatchConfig) +// { +// int logWatchTime = 0; +// try +// { +// logWatchTime = Integer.parseInt(logWatchConfig); +// } +// catch (NumberFormatException e) +// { +// _devlog.error("Log watch configuration value of " + logWatchConfig + " is invalid. Must be " +// + "a non-negative integer. Using default of zero (no watching configured"); +// } +// +// if (logConfigFile.exists() && logConfigFile.canRead()) +// { +// _devlog.info("Configuring logger using configuration file " + logConfigFile.getAbsolutePath()); +// if (logWatchTime > 0) +// { +// _devlog.info("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every " +// + logWatchTime + " seconds"); +// // log4j expects the watch interval in milliseconds +// DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000); +// } +// else +// { +// DOMConfigurator.configure(logConfigFile.getAbsolutePath()); +// } +// } +// else +// { +// System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath()); +// System.err.println("Using basic log4j configuration"); +// BasicConfigurator.configure(); +// } +// } + + public File getConfigFile() + { + return _configFile; + } + + + public class InitException extends Exception + { + InitException(String msg, Throwable cause) + { + super(msg, cause); + } + } +} \ No newline at end of file diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java new file mode 100644 index 0000000000..fc6057afd2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.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. + * + */ +/* + * + * 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; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.apache.commons.configuration.Configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +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.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.management.ManagedBroker; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * 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 MessageStore _messageStore; + + private final VirtualHost.VirtualHostMBean _virtualHostMBean; + + @MBeanConstructor("Creates the Broker Manager MBean") + public AMQBrokerManagerMBean(VirtualHost.VirtualHostMBean virtualHostMBean) throws JMException + { + super(ManagedBroker.class, ManagedBroker.TYPE); + + _virtualHostMBean = virtualHostMBean; + VirtualHost virtualHost = virtualHostMBean.getVirtualHost(); + + _queueRegistry = virtualHost.getQueueRegistry(); + _exchangeRegistry = virtualHost.getExchangeRegistry(); + _messageStore = virtualHost.getMessageStore(); + _exchangeFactory = virtualHost.getExchangeFactory(); + } + + public String getObjectInstanceName() + { + return _virtualHostMBean.getVirtualHost().getName(); + } + + /** + * Creates new exchange and registers it with the registry. + * + * @param exchangeName + * @param type + * @param durable + * @throws JMException + */ + public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException + { + 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); + } + else + { + throw new JMException("The exchange \"" + exchangeName + "\" already exists."); + } + } + } + catch (AMQException ex) + { + throw new MBeanException(ex, "Error in creating exchange " + exchangeName); + } + } + + /** + * Unregisters the exchange from registry. + * + * @param exchangeName + * @throws JMException + */ + public void unregisterExchange(String exchangeName) throws JMException + { + // TODO + // Check if the exchange is in use. + // boolean inUse = false; + // Check if there are queue-bindings with the exchange and unregister + // when there are no bindings. + try + { + _exchangeRegistry.unregisterExchange(new AMQShortString(exchangeName), false); + } + catch (AMQException ex) + { + throw new MBeanException(ex, "Error in unregistering exchange " + exchangeName); + } + } + + /** + * 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 + */ + public void createNewQueue(String queueName, String owner, boolean durable) throws JMException + { + AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); + if (queue != null) + { + throw new JMException("The queue \"" + queueName + "\" already exists."); + } + + try + { + AMQShortString ownerShortString = null; + if (owner != null) + { + ownerShortString = new AMQShortString(owner); + } + + queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost(), + null); + if (queue.isDurable() && !queue.isAutoDelete()) + { + _messageStore.createQueue(queue); + } + + _queueRegistry.registerQueue(queue); + } + catch (AMQException ex) + { + JMException jme = new JMException(ex.getMessage()); + jme.initCause(ex); + throw new MBeanException(jme, "Error in creating queue " + queueName); + } + } + + private VirtualHost getVirtualHost() + { + return _virtualHostMBean.getVirtualHost(); + } + + /** + * Deletes the queue from queue registry and persistant storage. + * + * @param queueName + * @throws JMException + */ + public void deleteQueue(String queueName) throws JMException + { + AMQQueue queue = _queueRegistry.getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("The Queue " + queueName + " is not a registerd queue."); + } + + try + { + queue.delete(); + _messageStore.removeQueue(queue); + + } + catch (AMQException ex) + { + JMException jme = new JMException(ex.getMessage()); + jme.initCause(ex); + throw new MBeanException(jme, "Error in deleting queue " + queueName); + } + } + + public ManagedObject getParentObject() + { + return _virtualHostMBean; + } + + // This will have a single instance for a virtual host, so not having the name property in the ObjectName + public ObjectName getObjectName() throws MalformedObjectNameException + { + return getObjectNameForSingleInstanceMBean(); + } +} // End of MBean class diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java new file mode 100644 index 0000000000..26ac562fb2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -0,0 +1,913 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.NoRouteException; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.flow.Pre0_10CreditManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.UnauthorizedAccessException; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.RecordDeliveryMethod; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.LocalTransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AMQChannel +{ + public static final int DEFAULT_PREFETCH = 5000; + + private static final Logger _log = Logger.getLogger(AMQChannel.class); + + private final int _channelId; + + + private final Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0l,0l); + + /** + * The delivery tag is unique per channel. This is pre-incremented before putting into the deliver frame so that + * value of this represents the last tag sent out + */ + private long _deliveryTag = 0; + + /** A channel has a default queue (the last declared) that is used when no queue name is explictily set */ + private AMQQueue _defaultQueue; + + /** This tag is unique per subscription to a queue. The server returns this in response to a basic.consume request. */ + private int _consumerTag; + + /** + * The current message - which may be partial in the sense that not all frames have been received yet - which has + * been received by this channel. As the frames are received the message gets updated and once all frames have been + * received the message can then be routed. + */ + private IncomingMessage _currentMessage; + + /** Maps from consumer tag to subscription instance. Allows us to unsubscribe from a queue. */ + protected final Map _tag2SubscriptionMap = new HashMap(); + + private final MessageStore _messageStore; + + private UnacknowledgedMessageMap _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(DEFAULT_PREFETCH); + + private final AtomicBoolean _suspended = new AtomicBoolean(false); + + private TransactionalContext _txnContext; + + /** + * A context used by the message store enabling it to track context for a given channel even across thread + * boundaries + */ + private final StoreContext _storeContext; + + private final List _returnMessages = new LinkedList(); + + private MessageHandleFactory _messageHandleFactory = new MessageHandleFactory(); + + // Why do we need this reference ? - ritchiem + private final AMQProtocolSession _session; + private boolean _closing; + + public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore) + throws AMQException + { + //Set values from configuration + Configurator.configure(this); + + _session = session; + _channelId = channelId; + _storeContext = new StoreContext("Session: " + session.getClientIdentifier() + "; channel: " + channelId); + + + _messageStore = messageStore; + + // by default the session is non-transactional + _txnContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); + } + + /** Sets this channel to be part of a local transaction */ + public void setLocalTransactional() + { + _txnContext = new LocalTransactionalContext(this); + } + + public boolean isTransactional() + { + // this does not look great but there should only be one "non-transactional" + // transactional context, while there could be several transactional ones in + // theory + return !(_txnContext instanceof NonTransactionalContext); + } + + public int getChannelId() + { + return _channelId; + } + + public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQException + { + + _currentMessage = new IncomingMessage(_messageStore.getNewMessageId(), info, _txnContext, _session); + _currentMessage.setMessageStore(_messageStore); + _currentMessage.setExchange(e); + } + + public void publishContentHeader(ContentHeaderBody contentHeaderBody) + throws AMQException + { + if (_currentMessage == null) + { + throw new AMQException("Received content header without previously receiving a BasicPublish frame"); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("Content header received on channel " + _channelId); + } + + _currentMessage.setContentHeaderBody(contentHeaderBody); + + _currentMessage.setExpiration(); + + routeCurrentMessage(); + + _currentMessage.routingComplete(_messageStore, _messageHandleFactory); + + deliverCurrentMessageIfComplete(); + + } + } + + private void deliverCurrentMessageIfComplete() + throws AMQException + { + // check and deliver if header says body length is zero + if (_currentMessage.allContentReceived()) + { + try + { + _currentMessage.deliverToQueues(); + } + catch (NoRouteException e) + { + _returnMessages.add(e); + } + catch(UnauthorizedAccessException ex) + { + _returnMessages.add(ex); + } + finally + { + // callback to allow the context to do any post message processing + // primary use is to allow message return processing in the non-tx case + _txnContext.messageProcessed(_session); + _currentMessage = null; + } + } + + } + + public void publishContentBody(ContentBody contentBody) throws AMQException + { + if (_currentMessage == null) + { + throw new AMQException("Received content body without previously receiving a JmsPublishBody"); + } + + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Content body received on channel " + _channelId); + } + + try + { + + // returns true iff the message was delivered (i.e. if all data was + // received + _currentMessage.addContentBodyFrame( + _session.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk( + contentBody)); + + deliverCurrentMessageIfComplete(); + } + catch (AMQException e) + { + // we want to make sure we don't keep a reference to the message in the + // event of an error + _currentMessage = null; + throw e; + } + } + + protected void routeCurrentMessage() throws AMQException + { + try + { + _currentMessage.route(); + } + catch (NoRouteException e) + { + //_currentMessage.incrementReference(); + _returnMessages.add(e); + } + } + + public long getNextDeliveryTag() + { + return ++_deliveryTag; + } + + public int getNextConsumerTag() + { + return ++_consumerTag; + } + + /** + * Subscribe to a queue. We register all subscriptions in the channel so that if the channel is closed we can clean + * up all subscriptions, even if the client does not explicitly unsubscribe from all queues. + * + * @param tag the tag chosen by the client (if null, server will generate one) + * @param queue the queue to subscribe to + * @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 exclusive Flag requesting exclusive access to the queue + * @return the consumer tag. This is returned to the subscriber and used in subsequent unsubscribe requests + * + * @throws ConsumerTagNotUniqueException if the tag is not unique + * @throws AMQException if something goes wrong + */ + public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, boolean acks, + FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException, ConsumerTagNotUniqueException + { + if (tag == null) + { + tag = new AMQShortString("sgen_" + getNextConsumerTag()); + } + + if (_tag2SubscriptionMap.containsKey(tag)) + { + throw new ConsumerTagNotUniqueException(); + } + + Subscription subscription = + SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager); + + + // So to keep things straight we put before the call and catch all exceptions from the register and tidy up. + // We add before we register as the Async Delivery process may AutoClose the subscriber + // so calling _cT2QM.remove before we have done put which was after the register succeeded. + // So to keep things straight we put before the call and catch all exceptions from the register and tidy up. + + _tag2SubscriptionMap.put(tag, subscription); + + try + { + queue.registerSubscription(subscription, exclusive); + } + catch (AMQException e) + { + _tag2SubscriptionMap.remove(tag); + throw e; + } + return tag; + } + + /** + * Unsubscribe a consumer from a queue. + * @param consumerTag + * @return true if the consumerTag had a mapped queue that could be unregistered. + * @throws AMQException + */ + public boolean unsubscribeConsumer(AMQShortString consumerTag) throws AMQException + { + + Subscription sub = _tag2SubscriptionMap.remove(consumerTag); + if (sub != null) + { + try + { + sub.getSendLock(); + sub.getQueue().unregisterSubscription(sub); + } + finally + { + sub.releaseSendLock(); + } + return true; + } + else + { + _log.warn("Attempt to unsubscribe consumer with tag '"+consumerTag+"' which is not registered."); + } + return false; + } + + /** + * Called from the protocol session to close this channel and clean up. T + * + * @throws AMQException if there is an error during closure + */ + public void close() throws AMQException + { + _txnContext.rollback(); + unsubscribeAllConsumers(); + try + { + requeue(); + } + catch (AMQException e) + { + _log.error("Caught AMQException whilst attempting to reque:" + e); + } + + setClosing(true); + } + + private void setClosing(boolean closing) + { + _closing = closing; + } + + private void unsubscribeAllConsumers() throws AMQException + { + if (_log.isInfoEnabled()) + { + if (!_tag2SubscriptionMap.isEmpty()) + { + _log.info("Unsubscribing all consumers on channel " + toString()); + } + else + { + _log.info("No consumers to unsubscribe on channel " + toString()); + } + } + + for (Map.Entry me : _tag2SubscriptionMap.entrySet()) + { + if (_log.isInfoEnabled()) + { + _log.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString()); + } + + Subscription sub = me.getValue(); + + try + { + sub.getSendLock(); + sub.getQueue().unregisterSubscription(sub); + } + finally + { + sub.releaseSendLock(); + } + + } + + _tag2SubscriptionMap.clear(); + } + + /** + * Add a message to the channel-based list of unacknowledged messages + * + * @param entry the record of the message on the queue that was delivered + * @param deliveryTag the delivery tag used when delivering the message (see protocol spec for description of the + * delivery tag) + * @param subscription The consumer that is to acknowledge this message. + */ + public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, Subscription subscription) + { + if (_log.isDebugEnabled()) + { + if (entry.getQueue() == null) + { + _log.debug("Adding unacked message with a null queue:" + entry.debugIdentity()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + " Adding unacked message(" + entry.getMessage().toString() + " DT:" + deliveryTag + + ") with a queue(" + entry.getQueue() + ") for " + subscription); + } + } + } + + _unacknowledgedMessageMap.add(deliveryTag, entry); + + } + + private final String id = "(" + System.identityHashCode(this) + ")"; + + public String debugIdentity() + { + return _channelId + id; + } + + /** + * Called to attempt re-delivery all outstanding unacknowledged messages on the channel. May result in delivery to + * this same channel or to other subscribers. + * + * @throws org.apache.qpid.AMQException if the requeue fails + */ + public void requeue() throws AMQException + { + // we must create a new map since all the messages will get a new delivery tag when they are redelivered + Collection messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages(); + + // Deliver these messages out of the transaction as their delivery was never + // part of the transaction only the receive. + TransactionalContext deliveryContext = null; + + if (!messagesToBeDelivered.isEmpty()) + { + if (_log.isInfoEnabled()) + { + _log.info("Requeuing " + messagesToBeDelivered.size() + " unacked messages. for " + toString()); + } + + if (!(_txnContext instanceof NonTransactionalContext)) + { + + deliveryContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); + } + else + { + deliveryContext = _txnContext; + } + } + + for (QueueEntry unacked : messagesToBeDelivered) + { + if (!unacked.isQueueDeleted()) + { + // Mark message redelivered + unacked.getMessage().setRedelivered(true); + + // Ensure message is released for redelivery + unacked.release(); + + // Deliver Message + deliveryContext.requeue(unacked); + + } + else + { + unacked.discard(_storeContext); + } + } + + } + + /** + * Requeue a single message + * + * @param deliveryTag The message to requeue + * + * @throws AMQException If something goes wrong. + */ + public void requeue(long deliveryTag) throws AMQException + { + QueueEntry unacked = _unacknowledgedMessageMap.remove(deliveryTag); + + if (unacked != null) + { + // Mark message redelivered + unacked.getMessage().setRedelivered(true); + + // Ensure message is released for redelivery + if (!unacked.isQueueDeleted()) + { + unacked.release(); + } + + + // Deliver these messages out of the transaction as their delivery was never + // part of the transaction only the receive. + TransactionalContext deliveryContext; + if (!(_txnContext instanceof NonTransactionalContext)) + { + + deliveryContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); + + } + else + { + deliveryContext = _txnContext; + } + + if (!unacked.isQueueDeleted()) + { + // Redeliver the messages to the front of the queue + deliveryContext.requeue(unacked); + // Deliver increments the message count but we have already deliverted this once so don't increment it again + // this was because deliver did an increment changed this. + } + else + { + _log.warn(System.identityHashCode(this) + " Requested requeue of message(" + unacked.getMessage().debugIdentity() + + "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message."); + + unacked.discard(_storeContext); + } + } + else + { + _log.warn("Requested requeue of message:" + deliveryTag + " but no such delivery tag exists." + + _unacknowledgedMessageMap.size()); + + } + + } + + /** + * Called to resend all outstanding unacknowledged messages to this same channel. + * + * @param requeue Are the messages to be requeued or dropped. + * + * @throws AMQException When something goes wrong. + */ + public void resend(final boolean requeue) throws AMQException + { + + + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + if (_log.isDebugEnabled()) + { + _log.debug("unacked map Size:" + _unacknowledgedMessageMap.size()); + } + + // Process the Unacked-Map. + // Marking messages who still have a consumer for to be resent + // and those that don't to be requeued. + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, requeue, _storeContext)); + + + // Process Messages to Resend + if (_log.isDebugEnabled()) + { + if (!msgToResend.isEmpty()) + { + _log.debug("Preparing (" + msgToResend.size() + ") message to resend."); + } + else + { + _log.debug("No message to resend."); + } + } + + for (Map.Entry entry : msgToResend.entrySet()) + { + QueueEntry message = entry.getValue(); + long deliveryTag = entry.getKey(); + + + + AMQMessage msg = message.getMessage(); + AMQQueue queue = message.getQueue(); + + // Our Java Client will always suspend the channel when resending! + // If the client has requested the messages be resent then it is + // their responsibility to ensure that thay are capable of receiving them + // i.e. The channel hasn't been server side suspended. + // if (isSuspended()) + // { + // _log.info("Channel is suspended so requeuing"); + // //move this message to requeue + // msgToRequeue.add(message); + // } + // else + // { + // release to allow it to be delivered + + // Without any details from the client about what has been processed we have to mark + // all messages in the unacked map as redelivered. + msg.setRedelivered(true); + + Subscription sub = message.getDeliveredSubscription(); + + if (sub != null) + { + + if(!queue.resend(message, sub)) + { + msgToRequeue.put(deliveryTag, message); + } + } + else + { + + if (_log.isInfoEnabled()) + { + _log.info("DeliveredSubscription not recorded so just requeueing(" + message.toString() + + ")to prevent loss"); + } + // move this message to requeue + msgToRequeue.put(deliveryTag, message); + } + } // for all messages + // } else !isSuspend + + if (_log.isInfoEnabled()) + { + if (!msgToRequeue.isEmpty()) + { + _log.info("Preparing (" + msgToRequeue.size() + ") message to requeue to."); + } + } + + // Deliver these messages out of the transaction as their delivery was never + // part of the transaction only the receive. + TransactionalContext deliveryContext; + if (!(_txnContext instanceof NonTransactionalContext)) + { + + deliveryContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages); + } + else + { + deliveryContext = _txnContext; + } + + // Process Messages to Requeue at the front of the queue + for (Map.Entry entry : msgToRequeue.entrySet()) + { + QueueEntry message = entry.getValue(); + long deliveryTag = entry.getKey(); + + message.release(); + message.setRedelivered(true); + + deliveryContext.requeue(message); + + _unacknowledgedMessageMap.remove(deliveryTag); + } + } + + /** + * Callback indicating that a queue has been deleted. We must update the structure of unacknowledged messages to + * remove the queue reference and also decrement any message reference counts, without actually removing the item + * since we may get an ack for a delivery tag that was generated from the deleted queue. + * + * @param queue the queue that has been deleted + * + */ + /* public void queueDeleted(final AMQQueue queue) + { + try + { + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + { + public boolean callback(UnacknowledgedMessage message) + { + if (message.getQueue() == queue) + { + try + { + message.discard(_storeContext); + message.setQueueDeleted(true); + + } + catch (AMQException e) + { + _log.error( + "Error decrementing ref count on message " + message.getMessage().getMessageId() + ": " + e, e); + throw new RuntimeException(e); + } + } + + return false; + } + + public void visitComplete() + { + } + }); + } + catch (AMQException e) + { + _log.error("Unexpected Error while handling deletion of queue", e); + throw new RuntimeException(e); + } + + } +*/ + /** + * Acknowledge one or more messages. + * + * @param deliveryTag the last delivery tag + * @param multiple if true will acknowledge all messages up to an including the delivery tag. if false only + * acknowledges the single message specified by the delivery tag + * + * @throws AMQException if the delivery tag is unknown (e.g. not outstanding) on this channel + */ + public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException + { + _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext); + } + + /** + * Used only for testing purposes. + * + * @return the map of unacknowledged messages + */ + public UnacknowledgedMessageMap getUnacknowledgedMessageMap() + { + return _unacknowledgedMessageMap; + } + + + public void setSuspended(boolean suspended) + { + + + boolean wasSuspended = _suspended.getAndSet(suspended); + if (wasSuspended != suspended) + { + if (wasSuspended) + { + // may need to deliver queued messages + for (Subscription s : _tag2SubscriptionMap.values()) + { + s.getQueue().deliverAsync(s); + } + } + } + } + + public boolean isSuspended() + { + return _suspended.get(); + } + + public void commit() throws AMQException + { + if (!isTransactional()) + { + throw new AMQException("Fatal error: commit called on non-transactional channel"); + } + + _txnContext.commit(); + } + + public void rollback() throws AMQException + { + _txnContext.rollback(); + } + + public String toString() + { + return "["+_session.toString()+":"+_channelId+"]"; + } + + public void setDefaultQueue(AMQQueue queue) + { + _defaultQueue = queue; + } + + public AMQQueue getDefaultQueue() + { + return _defaultQueue; + } + + public StoreContext getStoreContext() + { + return _storeContext; + } + + public void processReturns() throws AMQException + { + if (!_returnMessages.isEmpty()) + { + for (RequiredDeliveryException bouncedMessage : _returnMessages) + { + AMQMessage message = bouncedMessage.getAMQMessage(); + _session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(), + new AMQShortString(bouncedMessage.getMessage())); + + message.decrementReference(_storeContext); + } + + _returnMessages.clear(); + } + } + + + public TransactionalContext getTransactionalContext() + { + return _txnContext; + } + + public boolean isClosing() + { + return _closing; + } + + public AMQProtocolSession getProtocolSession() + { + return _session; + } + + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + + public void setCredit(final long prefetchSize, final int prefetchCount) + { + _creditManager.setCreditLimits(prefetchSize, prefetchCount); + } + + public List getReturnMessages() + { + return _returnMessages; + } + + public MessageStore getMessageStore() + { + return _messageStore; + } + + private final ClientDeliveryMethod _clientDeliveryMethod = new ClientDeliveryMethod() + { + + public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) + throws AMQException + { + getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannelId(), deliveryTag, sub.getConsumerTag()); + } + }; + + public ClientDeliveryMethod getClientDeliveryMethod() + { + return _clientDeliveryMethod; + } + + private final RecordDeliveryMethod _recordDeliveryMethod = new RecordDeliveryMethod() + { + + public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag) + { + addUnacknowledgedMessage(entry, deliveryTag, sub); + } + }; + + public RecordDeliveryMethod getRecordDeliveryMethod() + { + return _recordDeliveryMethod; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java new file mode 100644 index 0000000000..9a98af5689 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java @@ -0,0 +1,25 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server; + +public class ConsumerTagNotUniqueException extends Exception +{ +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ExtractResendAndRequeue.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ExtractResendAndRequeue.java new file mode 100644 index 0000000000..29494c4118 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ExtractResendAndRequeue.java @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; + +import java.util.Map; + +public class ExtractResendAndRequeue implements UnacknowledgedMessageMap.Visitor +{ + private static final Logger _log = Logger.getLogger(ExtractResendAndRequeue.class); + + private Map _msgToRequeue; + private Map _msgToResend; + private boolean _requeueIfUnabletoResend; + private StoreContext _storeContext; + private UnacknowledgedMessageMap _unacknowledgedMessageMap; + + public ExtractResendAndRequeue(UnacknowledgedMessageMap unacknowledgedMessageMap, + Map msgToRequeue, + Map msgToResend, + boolean requeueIfUnabletoResend, + StoreContext storeContext) + { + _unacknowledgedMessageMap = unacknowledgedMessageMap; + _msgToRequeue = msgToRequeue; + _msgToResend = msgToResend; + _requeueIfUnabletoResend = requeueIfUnabletoResend; + _storeContext = storeContext; + } + + public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException + { + + AMQMessage msg = message.getMessage(); + msg.setRedelivered(true); + final Subscription subscription = message.getDeliveredSubscription(); + if (subscription != null) + { + // Consumer exists + if (!subscription.isClosed()) + { + _msgToResend.put(deliveryTag, message); + } + else // consumer has gone + { + _msgToRequeue.put(deliveryTag, message); + } + } + else + { + // Message has no consumer tag, so was "delivered" to a GET + // or consumer no longer registered + // cannot resend, so re-queue. + if (!message.isQueueDeleted()) + { + if (_requeueIfUnabletoResend) + { + _msgToRequeue.put(deliveryTag, message); + } + else + { + message.discard(_storeContext); + _log.info("No DeadLetter Queue and requeue not requested so dropping message:" + message); + } + } + else + { + message.discard(_storeContext); + _log.warn("Message.queue is null and no DeadLetter Queue so dropping message:" + message); + } + } + + // false means continue processing + return false; + } + + public void visitComplete() + { + _unacknowledgedMessageMap.clear(); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java new file mode 100644 index 0000000000..f3b54034e7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -0,0 +1,554 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Logger; +import org.apache.log4j.xml.DOMConfigurator; +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.FixedSizeByteBufferAllocator; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.management.JMXManagedObjectRegistry; +import org.apache.qpid.server.protocol.AMQPFastProtocolHandler; +import org.apache.qpid.server.protocol.AMQPProtocolProvider; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.server.transport.ConnectorConfiguration; +import org.apache.qpid.url.URLSyntaxException; + +import java.io.File; +import java.io.IOException; +import java.net.BindException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.List; + +/** + * Main entry point for AMQPD. + * + */ +@SuppressWarnings({"AccessStaticViaInstance"}) +public class Main +{ + private static final Logger _logger = Logger.getLogger(Main.class); + public static final Logger _brokerLogger = Logger.getLogger("Qpid.Broker"); + + private static final String DEFAULT_CONFIG_FILE = "etc/config.xml"; + + private static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml"; + public static final String QPID_HOME = "QPID_HOME"; + private static final int IPV4_ADDRESS_LENGTH = 4; + + private static final char IPV4_LITERAL_SEPARATOR = '.'; + + protected static class InitException extends Exception + { + InitException(String msg, Throwable cause) + { + super(msg, cause); + } + } + + protected final Options options = new Options(); + protected CommandLine commandLine; + + protected Main(String[] args) + { + setOptions(options); + if (parseCommandline(args)) + { + execute(); + } + } + + protected boolean parseCommandline(String[] args) + { + try + { + commandLine = new PosixParser().parse(options, args); + + return true; + } + catch (ParseException e) + { + System.err.println("Error: " + e.getMessage()); + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("Qpid", options, true); + + return false; + } + } + + protected void setOptions(Options options) + { + Option help = new Option("h", "help", false, "print this message"); + Option version = new Option("v", "version", false, "print the version information and exit"); + Option configFile = + OptionBuilder.withArgName("file").hasArg().withDescription("use given configuration file").withLongOpt("config") + .create("c"); + Option port = + OptionBuilder.withArgName("port").hasArg() + .withDescription("listen on the specified port. Overrides any value in the config file") + .withLongOpt("port").create("p"); + Option mport = + OptionBuilder.withArgName("mport").hasArg() + .withDescription("listen on the specified management port. Overrides any value in the config file") + .withLongOpt("mport").create("m"); + + + Option bind = + OptionBuilder.withArgName("bind").hasArg() + .withDescription("bind to the specified address. Overrides any value in the config file") + .withLongOpt("bind").create("b"); + Option logconfig = + OptionBuilder.withArgName("logconfig").hasArg() + .withDescription("use the specified log4j xml configuration file. By " + + "default looks for a file named " + DEFAULT_LOG_CONFIG_FILENAME + + " in the same directory as the configuration file").withLongOpt("logconfig").create("l"); + Option logwatchconfig = + OptionBuilder.withArgName("logwatch").hasArg() + .withDescription("monitor the log file configuration file for changes. Units are seconds. " + + "Zero means do not check for changes.").withLongOpt("logwatch").create("w"); + + options.addOption(help); + options.addOption(version); + options.addOption(configFile); + options.addOption(logconfig); + options.addOption(logwatchconfig); + options.addOption(port); + options.addOption(mport); + options.addOption(bind); + } + + protected void execute() + { + // note this understands either --help or -h. If an option only has a long name you can use that but if + // an option has a short name and a long name you must use the short name here. + if (commandLine.hasOption("h")) + { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("Qpid", options, true); + } + else if (commandLine.hasOption("v")) + { + String ver = QpidProperties.getVersionString(); + + StringBuilder protocol = new StringBuilder("AMQP version(s) [major.minor]: "); + + boolean first = true; + for (ProtocolVersion pv : ProtocolVersion.getSupportedProtocolVersions()) + { + if (first) + { + first = false; + } + else + { + protocol.append(", "); + } + + protocol.append(pv.getMajorVersion()).append('-').append(pv.getMinorVersion()); + + } + + System.out.println(ver + " (" + protocol + ")"); + } + else + { + try + { + startup(); + } + catch (InitException e) + { + System.out.println(e.getMessage()); + _brokerLogger.error("Initialisation Error : " + e.getMessage()); + shutdown(1); + } + catch (ConfigurationException e) + { + System.out.println("Error configuring message broker: " + e); + _brokerLogger.error("Error configuring message broker: " + e); + e.printStackTrace(); + shutdown(1); + } + catch (Throwable e) + { + System.out.println("Error initialising message broker: " + e); + _brokerLogger.error("Error initialising message broker: " + e); + e.printStackTrace(); + shutdown(1); + } + } + } + + protected void shutdown(int status) + { + ApplicationRegistry.removeAll(); + System.exit(status); + } + + protected void startup() throws InitException, ConfigurationException, Exception + { + final String QpidHome = System.getProperty(QPID_HOME); + final File defaultConfigFile = new File(QpidHome, DEFAULT_CONFIG_FILE); + final File configFile = new File(commandLine.getOptionValue("c", defaultConfigFile.getPath())); + if (!configFile.exists()) + { + String error = "File " + configFile + " could not be found. Check the file exists and is readable."; + + if (QpidHome == null) + { + error = error + "\nNote: " + QPID_HOME + " is not set."; + } + + throw new InitException(error, null); + } + else + { + System.out.println("Using configuration file " + configFile.getAbsolutePath()); + } + + String logConfig = commandLine.getOptionValue("l"); + String logWatchConfig = commandLine.getOptionValue("w", "0"); + if (logConfig != null) + { + File logConfigFile = new File(logConfig); + configureLogging(logConfigFile, logWatchConfig); + } + else + { + File configFileDirectory = configFile.getParentFile(); + File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME); + configureLogging(logConfigFile, logWatchConfig); + } + + ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile); + + + updateManagementPort(config.getConfiguration(), commandLine.getOptionValue("m")); + + + + ApplicationRegistry.initialise(config); + + + //fixme .. use QpidProperties.getVersionString when we have fixed the classpath issues + // that are causing the broker build to pick up the wrong properties file and hence say + // Starting Qpid Client + _brokerLogger.info("Starting Qpid Broker " + QpidProperties.getReleaseVersion() + + " build: " + QpidProperties.getBuildVersion()); + + ConnectorConfiguration connectorConfig = + ApplicationRegistry.getInstance().getConfiguredObject(ConnectorConfiguration.class); + + ByteBuffer.setUseDirectBuffers(connectorConfig.enableDirectBuffers); + + // the MINA default is currently to use the pooled allocator although this may change in future + // once more testing of the performance of the simple allocator has been done + if (!connectorConfig.enablePooledAllocator) + { + ByteBuffer.setAllocator(new FixedSizeByteBufferAllocator()); + } + + + if(connectorConfig.useBiasedWrites) + { + System.setProperty("org.apache.qpid.use_write_biased_pool","true"); + } + + int port = connectorConfig.port; + + String portStr = commandLine.getOptionValue("p"); + if (portStr != null) + { + try + { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) + { + throw new InitException("Invalid port: " + portStr, e); + } + } + + String VIRTUAL_HOSTS = "virtualhosts"; + + Object virtualHosts = ApplicationRegistry.getInstance().getConfiguration().getProperty(VIRTUAL_HOSTS); + + if (virtualHosts != null) + { + if (virtualHosts instanceof Collection) + { + int totalVHosts = ((Collection) virtualHosts).size(); + for (int vhost = 0; vhost < totalVHosts; vhost++) + { + setupVirtualHosts(configFile.getParent(), (String) ((List) virtualHosts).get(vhost)); + } + } + else + { + setupVirtualHosts(configFile.getParent(), (String) virtualHosts); + } + } + + bind(port, connectorConfig); + + } + + /** + * Update the configuration data with the management port. + * @param configuration + * @param managementPort The string from the command line + */ + private void updateManagementPort(Configuration configuration, String managementPort) + { + if (managementPort != null) + { + int mport; + int defaultMPort = configuration.getInt(JMXManagedObjectRegistry.MANAGEMENT_PORT_CONFIG_PATH); + try + { + mport = Integer.parseInt(managementPort); + configuration.setProperty(JMXManagedObjectRegistry.MANAGEMENT_PORT_CONFIG_PATH, mport); + } + catch (NumberFormatException e) + { + _logger.warn("Invalid management port: " + managementPort + " will use default:" + defaultMPort, e); + } + } + } + + protected void setupVirtualHosts(String configFileParent, String configFilePath) + throws ConfigurationException, AMQException, URLSyntaxException + { + String configVar = "${conf}"; + + if (configFilePath.startsWith(configVar)) + { + configFilePath = configFileParent + configFilePath.substring(configVar.length()); + } + + if (configFilePath.indexOf(".xml") != -1) + { + VirtualHostConfiguration vHostConfig = new VirtualHostConfiguration(configFilePath); + vHostConfig.performBindings(); + } + else + { + // the virtualhosts value is a path. Search it for XML files. + + File virtualHostDir = new File(configFilePath); + + String[] fileNames = virtualHostDir.list(); + + for (int each = 0; each < fileNames.length; each++) + { + if (fileNames[each].endsWith(".xml")) + { + VirtualHostConfiguration vHostConfig = + new VirtualHostConfiguration(configFilePath + "/" + fileNames[each]); + vHostConfig.performBindings(); + } + } + } + } + + protected void bind(int port, ConnectorConfiguration connectorConfig) throws BindException + { + String bindAddr = commandLine.getOptionValue("b"); + if (bindAddr == null) + { + bindAddr = connectorConfig.bindAddress; + } + + try + { + // IoAcceptor acceptor = new SocketAcceptor(connectorConfig.processors); + IoAcceptor acceptor = connectorConfig.createAcceptor(); + SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig(); + SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig(); + + sc.setReceiveBufferSize(connectorConfig.socketReceiveBufferSize); + sc.setSendBufferSize(connectorConfig.socketWriteBuferSize); + sc.setTcpNoDelay(connectorConfig.tcpNoDelay); + + // if we do not use the executor pool threading model we get the default leader follower + // implementation provided by MINA + if (connectorConfig.enableExecutorPool) + { + sconfig.setThreadModel(ReadWriteThreadModel.getInstance()); + } + + if (!connectorConfig.enableSSL || !connectorConfig.sslOnly) + { + AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler(); + InetSocketAddress bindAddress; + if (bindAddr.equals("wildcard")) + { + bindAddress = new InetSocketAddress(port); + } + else + { + bindAddress = new InetSocketAddress(InetAddress.getByAddress(parseIP(bindAddr)), port); + } + + bind(acceptor, bindAddress, handler, sconfig); + + //fixme qpid.AMQP should be using qpidproperties to get value + _brokerLogger.info("Qpid.AMQP listening on non-SSL address " + bindAddress); + } + + if (connectorConfig.enableSSL) + { + AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler(); + try + { + + bind(acceptor, new InetSocketAddress(connectorConfig.sslPort), handler, sconfig); + + //fixme qpid.AMQP should be using qpidproperties to get value + _brokerLogger.info("Qpid.AMQP listening on SSL port " + connectorConfig.sslPort); + + } + catch (IOException e) + { + _brokerLogger.error("Unable to listen on SSL port: " + e, e); + } + } + + //fixme qpid.AMQP should be using qpidproperties to get value + _brokerLogger.info("Qpid Broker Ready :" + QpidProperties.getReleaseVersion() + + " build: " + QpidProperties.getBuildVersion()); + } + catch (Exception e) + { + _logger.error("Unable to bind service to registry: " + e, e); + //fixme this need tidying up + throw new BindException(e.getMessage()); + } + } + + /** + * Ensure that any bound Acceptors are recorded in the registry so they can be closed later. + * + * @param acceptor + * @param bindAddress + * @param handler + * @param sconfig + * + * @throws IOException from the acceptor.bind command + */ + private void bind(IoAcceptor acceptor, InetSocketAddress bindAddress, AMQPFastProtocolHandler handler, SocketAcceptorConfig sconfig) throws IOException + { + acceptor.bind(bindAddress, handler, sconfig); + + ApplicationRegistry.getInstance().addAcceptor(bindAddress, acceptor); + } + + public static void main(String[] args) + { + + new Main(args); + } + + private byte[] parseIP(String address) throws Exception + { + char[] literalBuffer = address.toCharArray(); + int byteCount = 0; + int currByte = 0; + byte[] ip = new byte[IPV4_ADDRESS_LENGTH]; + for (int i = 0; i < literalBuffer.length; i++) + { + char currChar = literalBuffer[i]; + if ((currChar >= '0') && (currChar <= '9')) + { + currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF); + } + + if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length)) + { + ip[byteCount++] = (byte) currByte; + currByte = 0; + } + } + + if (byteCount != 4) + { + throw new Exception("Invalid IP address: " + address); + } + return ip; + } + + private void configureLogging(File logConfigFile, String logWatchConfig) + { + int logWatchTime = 0; + try + { + logWatchTime = Integer.parseInt(logWatchConfig); + } + catch (NumberFormatException e) + { + System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be " + + "a non-negative integer. Using default of zero (no watching configured"); + } + + if (logConfigFile.exists() && logConfigFile.canRead()) + { + System.out.println("Configuring logger using configuration file " + logConfigFile.getAbsolutePath()); + if (logWatchTime > 0) + { + System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every " + + logWatchTime + " seconds"); + // log4j expects the watch interval in milliseconds + DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000); + } + else + { + DOMConfigurator.configure(logConfigFile.getAbsolutePath()); + } + } + else + { + System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath()); + System.err.println("Using basic log4j configuration"); + BasicConfigurator.configure(); + } + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java new file mode 100644 index 0000000000..e76f9c3f6c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java @@ -0,0 +1,68 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server; + +import java.io.IOException; + +import javax.management.JMException; + +/** + * The managed interface exposed to allow management of channels. + * @author Bhupendra Bhardwaj + * @version 0.1 + */ +public interface ManagedChannel +{ + static final String TYPE = "Channel"; + + /** + * Tells whether the channel is transactional. + * @return true if the channel is transactional. + * @throws IOException + */ + boolean isTransactional() throws IOException; + + /** + * Tells the number of unacknowledged messages in this channel. + * @return number of unacknowledged messages. + * @throws IOException + */ + int getUnacknowledgedMessageCount() throws IOException; + + + //********** Operations *****************// + + /** + * Commits the transactions if the channel is transactional. + * @throws IOException + * @throws JMException + */ + void commitTransactions() throws IOException, JMException; + + /** + * Rollsback the transactions if the channel is transactional. + * @throws IOException + * @throws JMException + */ + void rollbackTransactions() throws IOException, JMException; + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java new file mode 100644 index 0000000000..3f1947d65a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.queue.AMQMessage; + +/** + * Signals that a required delivery could not be made. This could be bacuse of the immediate flag being set and the + * queue having no consumers, or the mandatory flag being set and the exchange having no valid bindings. + * + *

The failed message is associated with this error condition, by taking a reference to it. This enables the + * correct compensating action to be taken against the message, for example, bouncing it back to the sender. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to deliver a message that must be delivered. + *
Associate the failed message with the error condition. {@link AMQMessage} + *
+ */ +public abstract class RequiredDeliveryException extends AMQException +{ + private AMQMessage _amqMessage; + + public RequiredDeliveryException(String message, AMQMessage payload) + { + super(message); + + setMessage(payload); + } + + + public RequiredDeliveryException(String message) + { + super(message); + } + + public void setMessage(final AMQMessage payload) + { + + // Increment the reference as this message is in the routing phase + // and so will have the ref decremented as routing fails. + // we need to keep this message around so we can return it in the + // handler. So increment here. + _amqMessage = payload.takeReference(); + + } + + public AMQMessage getAMQMessage() + { + return _amqMessage; + } + + public AMQConstant getErrorCode() + { + return getReplyCode(); + } + + public abstract AMQConstant getReplyCode(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java new file mode 100644 index 0000000000..db3a05eb52 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java @@ -0,0 +1,148 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ack; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.ArrayList; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.TxnOp; +import org.apache.qpid.server.queue.QueueEntry; + +/** + * A TxnOp implementation for handling accumulated acks + */ +public class TxAck implements TxnOp +{ + private final UnacknowledgedMessageMap _map; + private final Map _unacked = new HashMap(); + private List _individual; + private long _deliveryTag; + private boolean _multiple; + + public TxAck(UnacknowledgedMessageMap map) + { + _map = map; + } + + public void update(long deliveryTag, boolean multiple) + { + _unacked.clear(); + if (!multiple) + { + if(_individual == null) + { + _individual = new ArrayList(); + } + //have acked a single message that is not part of + //the previously acked region so record + //individually + _individual.add(deliveryTag);//_multiple && !multiple + } + else if (deliveryTag > _deliveryTag) + { + //have simply moved the last acked message on a + //bit + _deliveryTag = deliveryTag; + _multiple = true; + } + } + + public void consolidate() + { + if(_unacked.isEmpty()) + { + //lookup all the unacked messages that have been acked in this transaction + if (_multiple) + { + //get all the unacked messages for the accumulated + //multiple acks + _map.collect(_deliveryTag, true, _unacked); + } + if(_individual != null) + { + //get any unacked messages for individual acks outside the + //range covered by multiple acks + for (long tag : _individual) + { + if(_deliveryTag < tag) + { + _map.collect(tag, false, _unacked); + } + } + } + } + } + + public boolean checkPersistent() throws AMQException + { + consolidate(); + //if any of the messages in unacked are persistent the txn + //buffer must be marked as persistent: + for (QueueEntry msg : _unacked.values()) + { + if (msg.getMessage().isPersistent()) + { + return true; + } + } + return false; + } + + public void prepare(StoreContext storeContext) throws AMQException + { + //make persistent changes, i.e. dequeue and decrementReference + for (QueueEntry msg : _unacked.values()) + { + //Message has been ack so discard it. This will dequeue and decrement the reference. + msg.discard(storeContext); + + } + } + + public void undoPrepare() + { + //decrementReference is annoyingly untransactional (due to + //in memory counter) so if we failed in prepare for full + //txn, this op will have to compensate by fixing the count + //in memory (persistent changes will be rolled back by store) + for (QueueEntry msg : _unacked.values()) + { + msg.getMessage().takeReference(); + } + } + + public void commit(StoreContext storeContext) + { + //remove the unacked messages from the channels map + _map.remove(_unacked); + } + + public void rollback(StoreContext storeContext) + { + } +} + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java new file mode 100644 index 0000000000..c80a96f967 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ack; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.Map; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.StoreContext; + +public interface UnacknowledgedMessageMap +{ + public interface Visitor + { + /** + * @param deliveryTag + *@param message the message being iterated over @return true to stop iteration, false to continue + * @throws AMQException + */ + boolean callback(final long deliveryTag, QueueEntry message) throws AMQException; + + void visitComplete(); + } + + void visit(Visitor visitor) throws AMQException; + + void add(long deliveryTag, QueueEntry message); + + void collect(long deliveryTag, boolean multiple, Map msgs); + + boolean contains(long deliveryTag) throws AMQException; + + void remove(Map msgs); + + QueueEntry remove(long deliveryTag); + + public void drainTo(long deliveryTag, StoreContext storeContext) throws AMQException; + + Collection cancelAllMessages(); + + void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) throws AMQException; + + int size(); + + void clear(); + + QueueEntry get(long deliveryTag); + + /** + * Get the set of delivery tags that are outstanding. + * + * @return a set of delivery tags + */ + Set getDeliveryTags(); + + public long getUnacknowledgeBytes(); +} + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java new file mode 100644 index 0000000000..c567387662 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.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.ack; + +import org.apache.qpid.server.store.StoreContext; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.txn.TransactionalContext; + +public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap +{ + private final Object _lock = new Object(); + + private long _unackedSize; + + private Map _map; + + private long _lastDeliveryTag; + + private final int _prefetchLimit; + + public UnacknowledgedMessageMapImpl(int prefetchLimit) + { + _prefetchLimit = prefetchLimit; + _map = new LinkedHashMap(prefetchLimit); + } + + public void collect(long deliveryTag, boolean multiple, Map msgs) + { + if (multiple) + { + collect(deliveryTag, msgs); + } + else + { + msgs.put(deliveryTag, get(deliveryTag)); + } + + } + + public boolean contains(long deliveryTag) throws AMQException + { + synchronized (_lock) + { + return _map.containsKey(deliveryTag); + } + } + + public void remove(Map msgs) + { + synchronized (_lock) + { + for (Long deliveryTag : msgs.keySet()) + { + remove(deliveryTag); + } + } + } + + public QueueEntry remove(long deliveryTag) + { + synchronized (_lock) + { + + QueueEntry message = _map.remove(deliveryTag); + if(message != null) + { + _unackedSize -= message.getMessage().getSize(); + + } + + return message; + } + } + + public void visit(Visitor visitor) throws AMQException + { + synchronized (_lock) + { + Set> currentEntries = _map.entrySet(); + for (Map.Entry entry : currentEntries) + { + visitor.callback(entry.getKey().longValue(), entry.getValue()); + } + visitor.visitComplete(); + } + } + + public void add(long deliveryTag, QueueEntry message) + { + synchronized (_lock) + { + _map.put(deliveryTag, message); + _unackedSize += message.getMessage().getSize(); + _lastDeliveryTag = deliveryTag; + } + } + + public Collection cancelAllMessages() + { + synchronized (_lock) + { + Collection currentEntries = _map.values(); + _map = new LinkedHashMap(_prefetchLimit); + _unackedSize = 0l; + return currentEntries; + } + } + + public void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) + throws AMQException + { + synchronized (_lock) + { + txnContext.acknowledgeMessage(deliveryTag, _lastDeliveryTag, multiple, this); + } + } + + public int size() + { + synchronized (_lock) + { + return _map.size(); + } + } + + public void clear() + { + synchronized (_lock) + { + _map.clear(); + _unackedSize = 0l; + } + } + + public void drainTo(long deliveryTag, StoreContext storeContext) throws AMQException + + { + synchronized (_lock) + { + Iterator> it = _map.entrySet().iterator(); + while (it.hasNext()) + { + Map.Entry unacked = it.next(); + + if (unacked.getKey() > deliveryTag) + { + //This should not occur now. + throw new AMQException("UnacknowledgedMessageMap is out of order:" + unacked.getKey() + + " When deliveryTag is:" + deliveryTag + "ES:" + _map.entrySet().toString()); + } + + //Message has been ack so discard it. This will dequeue and decrement the reference. + unacked.getValue().discard(storeContext); + + it.remove(); + + _unackedSize -= unacked.getValue().getMessage().getSize(); + + + if (unacked.getKey() == deliveryTag) + { + break; + } + } + } + } + + public QueueEntry get(long key) + { + synchronized (_lock) + { + return _map.get(key); + } + } + + public Set getDeliveryTags() + { + synchronized (_lock) + { + return _map.keySet(); + } + } + + private void collect(long key, Map msgs) + { + synchronized (_lock) + { + for (Map.Entry entry : _map.entrySet()) + { + msgs.put(entry.getKey(),entry.getValue()); + if (entry.getKey() == key) + { + break; + } + } + } + } + + public long getUnacknowledgeBytes() + { + return _unackedSize; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java new file mode 100644 index 0000000000..31c1b61a21 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.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.configuration; + +import java.lang.reflect.Field; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.configuration.Configured; +import org.apache.qpid.configuration.PropertyException; +import org.apache.qpid.configuration.PropertyUtils; +import org.apache.qpid.server.registry.ApplicationRegistry; + +/** + * This class contains utilities for populating classes automatically from values pulled from configuration + * files. + */ +public class Configurator +{ + private static final Logger _logger = Logger.getLogger(Configurator.class); + + + /** + * Configure a given instance using the supplied configuration. Note that superclasses are not + * currently configured but this could easily be added if required. + * @param instance the instance to configure + * @param config the configuration to use to configure the object + */ + public static void configure(Object instance, Configuration config) + { + + for (Field f : instance.getClass().getDeclaredFields()) + { + Configured annotation = f.getAnnotation(Configured.class); + if (annotation != null) + { + setValueInField(f, instance, config, annotation); + } + } + } + + + + /** + * Configure a given instance using the application configuration. Note that superclasses are not + * currently configured but this could easily be added if required. + * @param instance the instance to configure + */ + public static void configure(Object instance) + { + configure(instance, ApplicationRegistry.getInstance().getConfiguration()); + } + + private static void setValueInField(Field f, Object instance, Configuration config, Configured annotation) + { + Class fieldClass = f.getType(); + String configPath = annotation.path(); + try + { + if (fieldClass == String.class) + { + String val = config.getString(configPath, annotation.defaultValue()); + val = PropertyUtils.replaceProperties(val); + f.set(instance, val); + } + else if (fieldClass == int.class) + { + int val = config.getInt(configPath, Integer.parseInt(annotation.defaultValue())); + f.setInt(instance, val); + } + else if (fieldClass == long.class) + { + long val = config.getLong(configPath, Long.parseLong(annotation.defaultValue())); + f.setLong(instance, val); + } + else if (fieldClass == double.class) + { + double val = config.getDouble(configPath, Double.parseDouble(annotation.defaultValue())); + f.setDouble(instance, val); + } + else if (fieldClass == boolean.class) + { + boolean val = config.getBoolean(configPath, Boolean.parseBoolean(annotation.defaultValue())); + f.setBoolean(instance, val); + } + else + { + _logger.error("Unsupported field type " + fieldClass + " for " + f + " IGNORING configured value"); + } + } + catch (PropertyException e) + { + _logger.error("Unable to expand property: " + e + " INGORING field " + f, e); + } + catch (IllegalAccessException e) + { + _logger.error("Unable to access field " + f + " IGNORING configured value"); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java new file mode 100644 index 0000000000..705e84752b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -0,0 +1,286 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.Collections; +import java.util.List; + +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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +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.virtualhost.VirtualHost; + +public class VirtualHostConfiguration +{ + private static final Logger _logger = Logger.getLogger(VirtualHostConfiguration.class); + + private static XMLConfiguration _config; + + private static final String VIRTUALHOST_PROPERTY_BASE = "virtualhost."; + + + public VirtualHostConfiguration(String configFile) throws ConfigurationException + { + _logger.info("Loading Config file:" + configFile); + + _config = new XMLConfiguration(configFile); + + } + + + + private void configureVirtualHost(String virtualHostName, Configuration configuration) throws ConfigurationException, AMQException + { + _logger.debug("Loding configuration for virtaulhost: "+virtualHostName); + + + VirtualHost virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost(virtualHostName); + + + + if(virtualHost == null) + { + throw new ConfigurationException("Unknown virtual host: " + virtualHostName); + } + + List exchangeNames = configuration.getList("exchanges.exchange.name"); + + for(Object exchangeNameObj : exchangeNames) + { + String exchangeName = String.valueOf(exchangeNameObj); + configureExchange(virtualHost, exchangeName, configuration); + } + + + List queueNames = configuration.getList("queues.queue.name"); + + for(Object queueNameObj : queueNames) + { + String queueName = String.valueOf(queueNameObj); + configureQueue(virtualHost, queueName, configuration); + } + + } + + private void configureExchange(VirtualHost virtualHost, String exchangeNameString, Configuration configuration) throws AMQException + { + + CompositeConfiguration exchangeConfiguration = new CompositeConfiguration(); + + exchangeConfiguration.addConfiguration(configuration.subset("exchanges.exchange."+ exchangeNameString)); + exchangeConfiguration.addConfiguration(configuration.subset("exchanges")); + + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore messageStore = virtualHost.getMessageStore(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + + AMQShortString exchangeName = new AMQShortString(exchangeNameString); + + + Exchange exchange; + + + + synchronized (exchangeRegistry) + { + exchange = exchangeRegistry.getExchange(exchangeName); + if(exchange == null) + { + + AMQShortString type = new AMQShortString(exchangeConfiguration.getString("type","direct")); + boolean durable = exchangeConfiguration.getBoolean("durable",false); + boolean autodelete = exchangeConfiguration.getBoolean("autodelete",false); + + Exchange newExchange = exchangeFactory.createExchange(exchangeName,type,durable,autodelete,0); + exchangeRegistry.registerExchange(newExchange); + } + + } + } + + public static CompositeConfiguration getDefaultQueueConfiguration(VirtualHost host) + { + CompositeConfiguration queueConfiguration = null; + if (_config == null) + return null; + + Configuration vHostConfiguration = _config.subset(VIRTUALHOST_PROPERTY_BASE + host.getName()); + + if (vHostConfiguration == null) + return null; + + Configuration defaultQueueConfiguration = vHostConfiguration.subset("queues"); + if (defaultQueueConfiguration != null) + { + queueConfiguration = new CompositeConfiguration(); + queueConfiguration.addConfiguration(defaultQueueConfiguration); + } + + return queueConfiguration; + } + + private void configureQueue(VirtualHost virtualHost, String queueNameString, Configuration configuration) throws AMQException, ConfigurationException + { + CompositeConfiguration queueConfiguration = new CompositeConfiguration(); + + queueConfiguration.addConfiguration(configuration.subset("queues.queue."+ queueNameString)); + queueConfiguration.addConfiguration(configuration.subset("queues")); + + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore messageStore = virtualHost.getMessageStore(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + + + AMQShortString queueName = new AMQShortString(queueNameString); + + AMQQueue queue; + + synchronized (queueRegistry) + { + queue = queueRegistry.getQueue(queueName); + + if (queue == null) + { + _logger.info("Creating queue '" + queueName + "' on virtual host " + virtualHost.getName()); + + boolean durable = queueConfiguration.getBoolean("durable" ,false); + boolean autodelete = queueConfiguration.getBoolean("autodelete", false); + String owner = queueConfiguration.getString("owner", null); + FieldTable arguments = null; + boolean priority = queueConfiguration.getBoolean("priority", false); + int priorities = queueConfiguration.getInt("priorities", -1); + if(priority || priorities > 0) + { + if(arguments == null) + { + arguments = new FieldTable(); + } + if (priorities < 0) + { + priorities = 10; + } + arguments.put(new AMQShortString("x-qpid-priorities"), priorities); + } + + + queue = AMQQueueFactory.createAMQQueueImpl(queueName, + durable, + owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */, + autodelete /* Therefore autodelete makes no sence */, + virtualHost, + arguments, + queueConfiguration); + + if (queue.isDurable()) + { + messageStore.createQueue(queue); + } + + queueRegistry.registerQueue(queue); + } + else + { + _logger.info("Queue '" + queueNameString + "' already exists on virtual host "+virtualHost.getName()+", not creating."); + } + + String exchangeName = queueConfiguration.getString("exchange", null); + + Exchange exchange = exchangeRegistry.getExchange(exchangeName == null ? null : new AMQShortString(exchangeName)); + + if(exchange == null) + { + exchange = virtualHost.getExchangeRegistry().getDefaultExchange(); + } + + if (exchange == null) + { + throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + exchangeName); + } + + synchronized (exchange) + { + List routingKeys = queueConfiguration.getList("routingKey"); + if(routingKeys == null || routingKeys.isEmpty()) + { + routingKeys = Collections.singletonList(queue.getName()); + } + + for(Object routingKeyNameObj : routingKeys) + { + AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj)); + + + queue.bind(exchange, routingKey, null); + + + _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + exchangeName + " RK:'" + routingKey + "'"); + } + + if(exchange != virtualHost.getExchangeRegistry().getDefaultExchange()) + { + queue.bind(virtualHost.getExchangeRegistry().getDefaultExchange(), queue.getName(), null); + } + } + + } + } + + + public void performBindings() throws AMQException, ConfigurationException + { + List virtualHostNames = _config.getList(VIRTUALHOST_PROPERTY_BASE + "name"); + String defaultVirtualHostName = _config.getString("default"); + if(defaultVirtualHostName != null) + { + ApplicationRegistry.getInstance().getVirtualHostRegistry().setDefaultVirtualHostName(defaultVirtualHostName); + } + _logger.info("Configuring " + virtualHostNames == null ? 0 : virtualHostNames.size() + " virtual hosts: " + virtualHostNames); + + for(Object nameObject : virtualHostNames) + { + String name = String.valueOf(nameObject); + configureVirtualHost(name, _config.subset(VIRTUALHOST_PROPERTY_BASE + name)); + } + + if (virtualHostNames == null || virtualHostNames.isEmpty()) + { + throw new ConfigurationException( + "Virtualhost Configuration document does not contain a valid virtualhost."); + } + } + + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java new file mode 100644 index 0000000000..d287595e2d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.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.connection; + +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.protocol.AMQConstant; + +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.List; + +public class ConnectionRegistry implements IConnectionRegistry +{ + private List _registry = new CopyOnWriteArrayList(); + + private VirtualHost _virtualHost; + + public ConnectionRegistry(VirtualHost virtualHost) + { + _virtualHost = virtualHost; + } + + public void initialise() + { + + } + + /** Close all of the currently open connections. */ + public void close() throws AMQException + { + while (!_registry.isEmpty()) + { + AMQProtocolSession connection = _registry.get(0); + + connection.closeConnection(0, new AMQConnectionException(AMQConstant.INTERNAL_ERROR, "Broker is shutting down", + 0, 0, + connection.getProtocolOutputConverter().getProtocolMajorVersion(), + connection.getProtocolOutputConverter().getProtocolMinorVersion(), + (Throwable) null), true); + } + } + + public void registerConnection(AMQProtocolSession connnection) + { + _registry.add(connnection); + } + + public void deregisterConnection(AMQProtocolSession connnection) + { + _registry.remove(connnection); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java new file mode 100644 index 0000000000..d64fde1c20 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.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.connection; + +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; + +public interface IConnectionRegistry +{ + + public void initialise(); + + public void close() throws AMQException; + + public void registerConnection(AMQProtocolSession connnection); + + public void deregisterConnection(AMQProtocolSession connnection); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java new file mode 100644 index 0000000000..8d24626b73 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.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.exchange; + +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.TabularType; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.ArrayType; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.management.ManagedObjectRegistry; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.List; +import java.util.Map; + +public abstract class AbstractExchange implements Exchange, Managable +{ + private AMQShortString _name; + + + + protected boolean _durable; + protected String _exchangeType; + protected int _ticket; + + private VirtualHost _virtualHost; + + protected ExchangeMBean _exchangeMbean; + + /** + * Whether the exchange is automatically deleted once all queues have detached from it + */ + protected boolean _autoDelete; + + /** + * 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. + */ + protected abstract class ExchangeMBean extends AMQManagedObject implements ManagedExchange + { + // open mbean data types for representing exchange bindings + protected String[] _bindingItemNames; + protected String[] _bindingItemIndexNames; + protected OpenType[] _bindingItemTypes; + protected CompositeType _bindingDataType; + protected TabularType _bindinglistDataType; + protected TabularDataSupport _bindingList; + + public ExchangeMBean() throws NotCompliantMBeanException + { + super(ManagedExchange.class, ManagedExchange.TYPE); + } + + protected void init() throws OpenDataException + { + _bindingItemNames = new String[]{"Binding Key", "Queue Names"}; + _bindingItemIndexNames = new String[]{_bindingItemNames[0]}; + + _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", + _bindingItemNames, _bindingItemNames, _bindingItemTypes); + _bindinglistDataType = new TabularType("Exchange Bindings", "Exchange Bindings for " + getName(), + _bindingDataType, _bindingItemIndexNames); + } + + public ManagedObject getParentObject() + { + return _virtualHost.getManagedObject(); + } + + public String getObjectInstanceName() + { + return _name.toString(); + } + + public String getName() + { + return _name.toString(); + } + + public String getExchangeType() + { + return _exchangeType; + } + + public Integer getTicketNo() + { + return _ticket; + } + + public boolean isDurable() + { + return _durable; + } + + public boolean isAutoDelete() + { + return _autoDelete; + } + + // 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=" + _exchangeType; + return new ObjectName(objNameString); + } + + protected ManagedObjectRegistry getManagedObjectRegistry() + { + return ApplicationRegistry.getInstance().getManagedObjectRegistry(); + } + } // End of MBean class + + public AMQShortString getName() + { + return _name; + } + + /** + * 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 ExchangeMBean createMBean() throws AMQException; + + public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException + { + _virtualHost = host; + _name = name; + _durable = durable; + _autoDelete = autoDelete; + _ticket = ticket; + _exchangeMbean = createMBean(); + _exchangeMbean.register(); + } + + public boolean isDurable() + { + return _durable; + } + + public boolean isAutoDelete() + { + return _autoDelete; + } + + public int getTicket() + { + return _ticket; + } + + public void close() throws AMQException + { + if (_exchangeMbean != null) + { + _exchangeMbean.unregister(); + } + } + + public String toString() + { + return getClass().getName() + "[" + getName() +"]"; + } + + public ManagedObject getManagedObject() + { + return _exchangeMbean; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public QueueRegistry getQueueRegistry() + { + return getVirtualHost().getQueueRegistry(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java new file mode 100644 index 0000000000..9d4c090971 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.commons.configuration.Configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class DefaultExchangeFactory implements ExchangeFactory +{ + private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class); + + private Map> _exchangeClassMap = new HashMap>(); + private final VirtualHost _host; + + public DefaultExchangeFactory(VirtualHost host) + { + _host = host; + registerExchangeType(DirectExchange.TYPE); + registerExchangeType(TopicExchange.TYPE); + registerExchangeType(HeadersExchange.TYPE); + registerExchangeType(FanoutExchange.TYPE); + } + + public void registerExchangeType(ExchangeType type) + { + _exchangeClassMap.put(type.getName(), type); + } + + public Collection> getRegisteredTypes() + { + return _exchangeClassMap.values(); + } + + public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, + int ticket) + throws AMQException + { + ExchangeType exchType = _exchangeClassMap.get(type); + if (exchType == null) + { + + throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); + } + Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete); + return e; + } + + public void initialise(Configuration hostConfig) + { + + if (hostConfig == null) + { + return; + } + + for(Object className : hostConfig.getList("custom-exchanges.class-name")) + { + try + { + ExchangeType exchangeType = ApplicationRegistry.getInstance().getPluginManager().getExchanges().get(String.valueOf(className)); + if (exchangeType == null) + { + _logger.error("No such custom exchange class found: \""+String.valueOf(className)+"\""); + return; + } + Class exchangeTypeClass = exchangeType.getClass(); + ExchangeType type = exchangeTypeClass.newInstance(); + registerExchangeType(type); + } + catch (ClassCastException classCastEx) + { + _logger.error("No custom exchange class: \""+String.valueOf(className)+"\" cannot be registered as it does not extend class \""+ExchangeType.class+"\""); + } + catch (IllegalAccessException e) + { + _logger.error("Cannot create custom exchange class: \""+String.valueOf(className)+"\"",e); + } + catch (InstantiationException e) + { + _logger.error("Cannot create custom exchange class: \""+String.valueOf(className)+"\"",e); + } + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java new file mode 100644 index 0000000000..0ab8208d88 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -0,0 +1,139 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.protocol.ExchangeInitialiser; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class DefaultExchangeRegistry implements ExchangeRegistry +{ + private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class); + + /** + * Maps from exchange name to exchange instance + */ + private ConcurrentMap _exchangeMap = new ConcurrentHashMap(); + + private Exchange _defaultExchange; + private VirtualHost _host; + + public DefaultExchangeRegistry(VirtualHost host) + { + //create 'standard' exchanges: + _host = host; + + } + + public void initialise() throws AMQException + { + new ExchangeInitialiser().initialise(_host.getExchangeFactory(), this); + } + + public MessageStore getMessageStore() + { + return _host.getMessageStore(); + } + + public void registerExchange(Exchange exchange) throws AMQException + { + _exchangeMap.put(exchange.getName(), exchange); + if (exchange.isDurable()) + { + getMessageStore().createExchange(exchange); + } + } + + public void setDefaultExchange(Exchange exchange) + { + _defaultExchange = exchange; + } + + public Exchange getDefaultExchange() + { + return _defaultExchange; + } + + public Collection getExchangeNames() + { + return _exchangeMap.keySet(); + } + + public void unregisterExchange(AMQShortString name, boolean inUse) throws AMQException + { + // TODO: check inUse argument + Exchange e = _exchangeMap.remove(name); + if (e != null) + { + if (e.isDurable()) + { + getMessageStore().removeExchange(e); + } + e.close(); + } + else + { + throw new AMQException("Unknown exchange " + name); + } + } + + public Exchange getExchange(AMQShortString name) + { + if ((name == null) || name.length() == 0) + { + return getDefaultExchange(); + } + else + { + return _exchangeMap.get(name); + } + + } + + /** + * Routes content through exchanges, delivering it to 1 or more queues. + * @param payload + * @throws AMQException if something goes wrong delivering data + */ + public void routeContent(IncomingMessage payload) throws AMQException + { + final AMQShortString exchange = payload.getExchange(); + final Exchange exch = getExchange(exchange); + // there is a small window of opportunity for the exchange to be deleted in between + // the BasicPublish being received (where the exchange is validated) and the final + // content body being received (which triggers this method) + // TODO: check where the exchange is validated + if (exch == null) + { + throw new AMQException("Exchange '" + exchange + "' does not exist"); + } + exch.route(payload); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java new file mode 100644 index 0000000000..e39c005750 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.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.exchange; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.management.JMException; +import javax.management.MBeanException; +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 org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class DirectExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(DirectExchange.class); + + /** + * Maps from queue name to queue instances + */ + private final Index _index = new Index(); + + public static final ExchangeType TYPE = new ExchangeType() + { + + public AMQShortString getName() + { + return ExchangeDefaults.DIRECT_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return DirectExchange.class; + } + + public DirectExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + DirectExchange exch = new DirectExchange(); + exch.initialise(host,name,durable,ticket,autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.DIRECT_EXCHANGE_NAME; + } + }; + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Direct Exchange") + private final class DirectExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ direct exchange") + public DirectExchangeMBean() throws JMException + { + super(); + _exchangeType = "direct"; + init(); + } + + public TabularData bindings() throws OpenDataException + { + Map> bindings = _index.getBindingsMap(); + _bindingList = new TabularDataSupport(_bindinglistDataType); + + for (Map.Entry> entry : bindings.entrySet()) + { + AMQShortString key = entry.getKey(); + List queueList = new ArrayList(); + + List queues = entry.getValue(); + for (AMQQueue q : queues) + { + queueList.add(q.getName().toString()); + } + + Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[0])}; + CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingData); + } + + return _bindingList; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(DirectExchange.this, new AMQShortString(binding), null); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + }// End of MBean class + + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new DirectExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the direct exchange mbean", ex); + throw new AMQException("Exception occured in creating the direct exchange mbean", ex); + } + } + + public AMQShortString getType() + { + return ExchangeDefaults.DIRECT_EXCHANGE_CLASS; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert routingKey != null; + if (!_index.add(routingKey, queue)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Queue (" + queue.getName() + ")" + queue + " is already registered with routing key " + routingKey); + } + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Binding queue(" + queue.getName() + ") " + queue + " with routing key " + routingKey + + (args == null ? "" : " and arguments " + args.toString()) + + " to exchange " + this); + } + } + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert routingKey != null; + + if (!_index.remove(routingKey, queue)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + + " with routing key " + routingKey + ". No queue was registered with that _routing key"); + } + } + + public void route(IncomingMessage payload) throws AMQException + { + + final AMQShortString routingKey = payload.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : payload.getRoutingKey(); + + final ArrayList queues = (routingKey == null) ? null : _index.get(routingKey); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + queues); + } + + payload.enqueue(queues); + + + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey,queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + final List queues = _index.get(routingKey); + return queues != null && queues.contains(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + final List queues = _index.get(routingKey); + return queues != null && !queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) + { + Map> bindings = _index.getBindingsMap(); + for (List queues : bindings.values()) + { + if (queues.contains(queue)) + { + return true; + } + } + return false; + } + + public boolean hasBindings() + { + return !_index.getBindingsMap().isEmpty(); + } + + public Map> getBindings() + { + return _index.getBindingsMap(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java new file mode 100644 index 0000000000..06209c5458 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.List; +import java.util.Map; + +public interface Exchange +{ + AMQShortString getName(); + + AMQShortString getType(); + + void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException; + + boolean isDurable(); + + /** + * @return true if the exchange will be deleted after all queues have been detached + */ + boolean isAutoDelete(); + + int getTicket(); + + void close() throws AMQException; + + void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException; + + void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException; + + void route(IncomingMessage message) throws AMQException; + + + /** + * Determines whether a message would be isBound to a particular queue using a specific routing key and arguments + * @param routingKey + * @param arguments + * @param queue + * @return + * @throws AMQException + */ + boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue); + + /** + * Determines whether a message would be isBound to a particular queue using a specific routing key + * @param routingKey + * @param queue + * @return + * @throws AMQException + */ + boolean isBound(AMQShortString routingKey, AMQQueue queue); + + /** + * Determines whether a message is routing to any queue using a specific _routing key + * @param routingKey + * @return + * @throws AMQException + */ + boolean isBound(AMQShortString routingKey); + + boolean isBound(AMQQueue queue); + + /** + * Returns true if this exchange has at least one binding associated with it. + * @return + * @throws AMQException + */ + boolean hasBindings(); + + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java new file mode 100644 index 0000000000..0bcfec7181 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.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.exchange; + +import java.util.Collection; + +import org.apache.commons.configuration.Configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; + + +public interface ExchangeFactory +{ + Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, + int ticket) + throws AMQException; + + void initialise(Configuration hostConfig); + + Collection> getRegisteredTypes(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java new file mode 100644 index 0000000000..c77f114428 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java @@ -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. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.qpid.AMQException; + +/** + * ExchangeInUseRegistry indicates that an exchange cannot be unregistered because it is currently being used. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to unregister exchange that is in use. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo This exception is not used. However, it is part of the ExchangeRegistry interface, and looks like code is + * going to need to be added to throw/deal with this. Alternatively ExchangeResitries may be able to handle the + * issue internally. + */ +public class ExchangeInUseException extends AMQException +{ + public ExchangeInUseException(String exchangeName) + { + super("Exchange " + exchangeName + " is currently in use"); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java new file mode 100644 index 0000000000..fe3b19e74e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; + +import java.util.Collection; + + +public interface ExchangeRegistry extends MessageRouter +{ + void registerExchange(Exchange exchange) throws AMQException; + + /** + * Unregister an exchange + * @param name name of the exchange to delete + * @param inUse if true, do NOT delete the exchange if it is in use (has queues bound to it) + * @throws ExchangeInUseException when the exchange cannot be deleted because it is in use + * @throws AMQException + */ + void unregisterExchange(AMQShortString name, boolean inUse) throws ExchangeInUseException, AMQException; + + Exchange getExchange(AMQShortString name); + + void setDefaultExchange(Exchange exchange); + + Exchange getDefaultExchange(); + + Collection getExchangeNames(); + + void initialise() throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java new file mode 100644 index 0000000000..0b55caa2f1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.framing.AMQShortString; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.virtualhost.VirtualHost; + + +public interface ExchangeType +{ + public AMQShortString getName(); + public Class getExchangeClass(); + public T newInstance(VirtualHost host, AMQShortString name, + boolean durable, int ticket, boolean autoDelete) throws AMQException; + public AMQShortString getDefaultExchangeName(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java new file mode 100644 index 0000000000..e9fd4d548b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -0,0 +1,224 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.IncomingMessage; +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.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +public class FanoutExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(FanoutExchange.class); + + /** + * Maps from queue name to queue instances + */ + private final CopyOnWriteArraySet _queues = new CopyOnWriteArraySet(); + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Fanout Exchange") + private final class FanoutExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ fanout exchange") + public FanoutExchangeMBean() throws JMException + { + super(); + _exchangeType = "fanout"; + init(); + } + + public TabularData bindings() throws OpenDataException + { + + _bindingList = new TabularDataSupport(_bindinglistDataType); + + for (AMQQueue queue : _queues) + { + String queueName = queue.getName().toString(); + + Object[] bindingItemValues = {queueName, new String[]{queueName}}; + CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingData); + } + + return _bindingList; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(FanoutExchange.this, new AMQShortString(binding), null); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + } // End of MBean class + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new FanoutExchange.FanoutExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the direct exchange mbean", ex); + throw new AMQException("Exception occured in creating the direct exchange mbean", ex); + } + } + + public static final ExchangeType TYPE = new ExchangeType() + { + + public AMQShortString getName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return FanoutExchange.class; + } + + public FanoutExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + FanoutExchange exch = new FanoutExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.FANOUT_EXCHANGE_NAME; + } + }; + + public Map> getBindings() + { + return null; + } + + public AMQShortString getType() + { + return ExchangeDefaults.FANOUT_EXCHANGE_CLASS; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (_queues.contains(queue)) + { + _logger.debug("Queue " + queue + " is already registered"); + } + else + { + _queues.add(queue); + _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this); + } + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + + if (!_queues.remove(queue)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". "); + } + } + + public void route(IncomingMessage payload) throws AMQException + { + + + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + _queues); + } + + payload.enqueue(new ArrayList(_queues)); + + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return _queues.contains(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + + return (_queues != null) && !_queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) + { + + return _queues.contains(queue); + } + + public boolean hasBindings() + { + return !_queues.isEmpty(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java new file mode 100644 index 0000000000..2b7df4361a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java @@ -0,0 +1,219 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.apache.qpid.framing.AMQTypedValue; +import org.apache.qpid.framing.FieldTable; + +/** + * Defines binding and matching based on a set of headers. + */ +class HeadersBinding +{ + private static final Logger _logger = Logger.getLogger(HeadersBinding.class); + + private final FieldTable _mappings; + private final Set required = new HashSet(); + private final Map matches = new HashMap(); + private boolean matchAny; + + private final class MatchesOrProcessor implements FieldTable.FieldTableElementProcessor + { + private Boolean _result = Boolean.FALSE; + + public boolean processElement(String propertyName, AMQTypedValue value) + { + if((value != null) && (value.getValue() != null) && value.getValue().equals(matches.get(propertyName))) + { + _result = Boolean.TRUE; + return false; + } + return true; + } + + public Object getResult() + { + return _result; + } + } + + private final class RequiredOrProcessor implements FieldTable.FieldTableElementProcessor + { + Boolean _result = Boolean.FALSE; + + public boolean processElement(String propertyName, AMQTypedValue value) + { + if(required.contains(propertyName)) + { + _result = Boolean.TRUE; + return false; + } + return true; + } + + public Object getResult() + { + return _result; + } + } + + + + /** + * Creates a binding for a set of mappings. Those mappings whose value is + * null or the empty string are assumed only to be required headers, with + * no constraint on the value. Those with a non-null value are assumed to + * define a required match of value. + * @param mappings the defined mappings this binding should use + */ + + HeadersBinding(FieldTable mappings) + { + _mappings = mappings; + initMappings(); + } + + private void initMappings() + { + + _mappings.processOverElements(new FieldTable.FieldTableElementProcessor() + { + + public boolean processElement(String propertyName, AMQTypedValue value) + { + if (isSpecial(propertyName)) + { + processSpecial(propertyName, value.getValue()); + } + else if (value.getValue() == null || value.getValue().equals("")) + { + required.add(propertyName); + } + else + { + matches.put(propertyName,value.getValue()); + } + + return true; + } + + public Object getResult() + { + return null; + } + }); + } + + protected FieldTable getMappings() + { + return _mappings; + } + + /** + * Checks whether the supplied headers match the requirements of this binding + * @param headers the headers to check + * @return true if the headers define any required keys and match any required + * values + */ + public boolean matches(FieldTable headers) + { + if(headers == null) + { + return required.isEmpty() && matches.isEmpty(); + } + else + { + return matchAny ? or(headers) : and(headers); + } + } + + private boolean and(FieldTable headers) + { + if(headers.keys().containsAll(required)) + { + for(Map.Entry e : matches.entrySet()) + { + if(!e.getValue().equals(headers.getObject(e.getKey()))) + { + return false; + } + } + return true; + } + else + { + return false; + } + } + + + private boolean or(final FieldTable headers) + { + if(required.isEmpty() || !(Boolean) headers.processOverElements(new RequiredOrProcessor())) + { + return ((!matches.isEmpty()) && (Boolean) headers.processOverElements(new MatchesOrProcessor())) + || (required.isEmpty() && matches.isEmpty()); + } + else + { + return true; + } + } + + private void processSpecial(String key, Object value) + { + if("X-match".equalsIgnoreCase(key)) + { + matchAny = isAny(value); + } + else + { + _logger.warn("Ignoring special header: " + key); + } + } + + private boolean isAny(Object value) + { + if(value instanceof String) + { + if("any".equalsIgnoreCase((String) value)) return true; + if("all".equalsIgnoreCase((String) value)) return false; + } + _logger.warn("Ignoring unrecognised match type: " + value); + return false;//default to all + } + + static boolean isSpecial(Object key) + { + return key instanceof String && isSpecial((String) key); + } + + static boolean isSpecial(String key) + { + return key.startsWith("X-") || key.startsWith("x-"); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java new file mode 100644 index 0000000000..1ee1f35de6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -0,0 +1,350 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQTypedValue; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import javax.management.JMException; +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.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * An exchange that binds queues based on a set of required headers and header values + * and routes messages to these queues by matching the headers of the message against + * those with which the queues were bound. + *

+ *

+ * The Headers Exchange
+ *
+ *  Routes messages according to the value/presence of fields in the message header table.
+ *  (Basic and JMS content has a content header field called "headers" that is a table of
+ *   message header fields).
+ *
+ *  class = "headers"
+ *  routing key is not used
+ *
+ *  Has the following binding arguments:
+ *
+ *  the X-match field - if "all", does an AND match (used for GRM), if "any", does an OR match.
+ *  other fields prefixed with "X-" are ignored (and generate a console warning message).
+ *  a field with no value or empty value indicates a match on presence only.
+ *  a field with a value indicates match on field presence and specific value.
+ *
+ *  Standard instances:
+ *
+ *  amq.match - pub/sub on field content/value
+ *  
+ */ +public class HeadersExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(HeadersExchange.class); + + + + public static final ExchangeType TYPE = new ExchangeType() + { + + public AMQShortString getName() + { + return ExchangeDefaults.HEADERS_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return HeadersExchange.class; + } + + public HeadersExchange newInstance(VirtualHost host, AMQShortString name, boolean durable, int ticket, + boolean autoDelete) throws AMQException + { + HeadersExchange exch = new HeadersExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + + return ExchangeDefaults.HEADERS_EXCHANGE_NAME; + } + }; + + + private final List _bindings = new CopyOnWriteArrayList(); + + /** + * HeadersExchangeMBean class implements the management interface for the + * Header Exchanges. + */ + @MBeanDescription("Management Bean for Headers Exchange") + private final class HeadersExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ Headers exchange") + public HeadersExchangeMBean() throws JMException + { + super(); + _exchangeType = "headers"; + init(); + } + + /** + * initialises the OpenType objects. + */ + protected void init() throws OpenDataException + { + _bindingItemNames = new String[]{"Binding No", "Queue Name", "Queue Bindings"}; + _bindingItemIndexNames = new String[]{_bindingItemNames[0]}; + + _bindingItemTypes = new OpenType[3]; + _bindingItemTypes[0] = SimpleType.INTEGER; + _bindingItemTypes[1] = SimpleType.STRING; + _bindingItemTypes[2] = new ArrayType(1, SimpleType.STRING); + _bindingDataType = new CompositeType("Exchange Binding", "Queue name and header bindings", + _bindingItemNames, _bindingItemNames, _bindingItemTypes); + _bindinglistDataType = new TabularType("Exchange Bindings", "List of exchange bindings for " + getName(), + _bindingDataType, _bindingItemIndexNames); + } + + public TabularData bindings() throws OpenDataException + { + _bindingList = new TabularDataSupport(_bindinglistDataType); + int count = 1; + for (Iterator itr = _bindings.iterator(); itr.hasNext();) + { + Registration registration = itr.next(); + String queueName = registration.queue.getName().toString(); + + HeadersBinding headers = registration.binding; + FieldTable headerMappings = headers.getMappings(); + final List mappingList = new ArrayList(); + + headerMappings.processOverElements(new FieldTable.FieldTableElementProcessor() + { + + public boolean processElement(String propertyName, AMQTypedValue value) + { + mappingList.add(propertyName + "=" + value.getValue()); + return true; + } + + public Object getResult() + { + return mappingList; + } + }); + + + Object[] bindingItemValues = {count++, queueName, mappingList.toArray(new String[0])}; + CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingData); + } + + return _bindingList; + } + + /** + * Creates bindings. Binding pattern is as follows- + * =,=,... + * @param queueName + * @param binding + * @throws javax.management.JMException + */ + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + String[] bindings = binding.split(","); + FieldTable bindingMap = new FieldTable(); + for (int i = 0; i < bindings.length; i++) + { + String[] keyAndValue = bindings[i].split("="); + if (keyAndValue == null || keyAndValue.length < 2) + { + throw new JMException("Format for headers binding should be \"=,=\" "); + } + bindingMap.setString(keyAndValue[0], keyAndValue[1]); + } + + _bindings.add(new Registration(new HeadersBinding(bindingMap), queue)); + } + + } // End of MBean class + + public AMQShortString getType() + { + return ExchangeDefaults.HEADERS_EXCHANGE_CLASS; + } + + public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + _logger.debug("Exchange " + getName() + ": Binding " + queue.getName() + " with " + args); + _bindings.add(new Registration(new HeadersBinding(args), queue)); + } + + public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + _logger.debug("Exchange " + getName() + ": Unbinding " + queue.getName()); + if(!_bindings.remove(new Registration(new HeadersBinding(args), queue))) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + + " with headers args " + args); + } + } + + public void route(IncomingMessage payload) throws AMQException + { + FieldTable headers = getHeaders(payload.getContentHeaderBody()); + if (_logger.isDebugEnabled()) + { + _logger.debug("Exchange " + getName() + ": routing message with headers " + headers); + } + boolean routed = false; + ArrayList queues = new ArrayList(); + for (Registration e : _bindings) + { + + if (e.binding.matches(headers)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Exchange " + getName() + ": delivering message with headers " + + headers + " to " + e.queue.getName()); + } + queues.add(e.queue); + + routed = true; + } + } + payload.enqueue(queues); + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + //fixme isBound here should take the arguements in to consideration. + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return isBound(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + return hasBindings(); + } + + public boolean isBound(AMQQueue queue) + { + for (Registration r : _bindings) + { + if (r.queue.equals(queue)) + { + return true; + } + } + return false; + } + + public boolean hasBindings() + { + return !_bindings.isEmpty(); + } + + protected FieldTable getHeaders(ContentHeaderBody contentHeaderFrame) + { + //what if the content type is not 'basic'? 'file' and 'stream' content classes also define headers, + //but these are not yet implemented. + return ((BasicContentHeaderProperties) contentHeaderFrame.properties).getHeaders(); + } + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new HeadersExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the HeadersExchangeMBean", ex); + throw new AMQException("Exception occured in creating the HeadersExchangeMBean", ex); + } + } + + public Map> getBindings() + { + return null; + } + + private static class Registration + { + private final HeadersBinding binding; + private final AMQQueue queue; + + Registration(HeadersBinding binding, AMQQueue queue) + { + this.binding = binding; + this.queue = queue; + } + + public int hashCode() + { + return queue.hashCode(); + } + + public boolean equals(Object o) + { + return o instanceof Registration && ((Registration) o).queue.equals(queue); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java new file mode 100644 index 0000000000..ec83161029 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java @@ -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. + * + */ +package org.apache.qpid.server.exchange; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.queue.AMQQueue; + +/** + * An index of queues against routing key. Allows multiple queues to be stored + * against the same key. Used in the DirectExchange. + */ +class Index +{ + private ConcurrentMap> _index + = new ConcurrentHashMap>(); + + synchronized boolean add(AMQShortString key, AMQQueue queue) + { + ArrayList queues = _index.get(key); + if(queues == null) + { + queues = new ArrayList(); + } + else + { + queues = new ArrayList(queues); + } + //next call is atomic, so there is no race to create the list + _index.put(key, queues); + + if(queues.contains(queue)) + { + return false; + } + else + { + return queues.add(queue); + } + } + + synchronized boolean remove(AMQShortString key, AMQQueue queue) + { + ArrayList queues = _index.get(key); + if (queues != null) + { + queues = new ArrayList(queues); + boolean removed = queues.remove(queue); + if(removed) + { + if (queues.size() == 0) + { + _index.remove(key); + } + else + { + _index.put(key, queues); + } + } + return removed; + } + return false; + } + + ArrayList get(AMQShortString key) + { + return _index.get(key); + } + + Map> getBindingsMap() + { + return new HashMap>(_index); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java new file mode 100644 index 0000000000..5d6d68b3c8 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import java.io.IOException; + +import javax.management.JMException; +import javax.management.MBeanOperationInfo; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.server.management.MBeanAttribute; +import org.apache.qpid.server.management.MBeanOperation; +import org.apache.qpid.server.management.MBeanOperationParameter; +import org.apache.qpid.server.queue.ManagedQueue; + +/** + * The management interface exposed to allow management of an Exchange. + * @author Robert J. Greig + * @author Bhupendra Bhardwaj + * @version 0.1 + */ +public interface ManagedExchange +{ + static final String TYPE = "Exchange"; + + /** + * Returns the name of the managed exchange. + * @return the name of the exchange. + * @throws IOException + */ + @MBeanAttribute(name="Name", description=TYPE + " Name") + String getName() throws IOException; + + @MBeanAttribute(name="ExchangeType", description="Exchange Type") + String getExchangeType() throws IOException; + + @MBeanAttribute(name="TicketNo", description="Exchange Ticket No") + Integer getTicketNo() throws IOException; + + /** + * Tells if the exchange is durable or not. + * @return true if the exchange is durable. + * @throws IOException + */ + @MBeanAttribute(name="Durable", description="true if Exchange is durable") + boolean isDurable() throws IOException; + + /** + * Tells if the exchange is set for autodelete or not. + * @return true if the exchange is set as autodelete. + * @throws IOException + */ + @MBeanAttribute(name="AutoDelete", description="true if Exchange is AutoDelete") + boolean isAutoDelete() throws IOException; + + // Operations + + /** + * Returns all the bindings this exchange has with the queues. + * @return the bindings with the exchange. + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="bindings", description="view the queue bindings for this exchange") + TabularData bindings() throws IOException, JMException; + + /** + * Creates new binding with the given queue and binding. + * @param queueName + * @param binding + * @throws JMException + */ + @MBeanOperation(name="createNewBinding", + description="create a new binding with this exchange", + impact= MBeanOperationInfo.ACTION) + void createNewBinding(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue name") String queueName, + @MBeanOperationParameter(name="Binding", description="New binding")String binding) + throws JMException; + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java new file mode 100644 index 0000000000..db9beb6da7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; + +/** + * Separated out from the ExchangeRegistry interface to allow components + * that use only this part to have a dependency with a reduced footprint. + * + */ +public interface MessageRouter +{ + /** + * Routes content through exchanges, delivering it to 1 or more queues. + * @param message the message to be routed + * + * @throws org.apache.qpid.AMQException if something goes wrong delivering data + */ + void routeContent(IncomingMessage message) throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java new file mode 100644 index 0000000000..d18ad7ab14 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.protocol.AMQConstant; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.IncomingMessage; + +/** + * NoRouteException is a {@link RequiredDeliveryException} that represents the failure case where a manadatory message + * cannot be delivered because there is no route for the message. The AMQP status code, 312, is always used to report + * this condition. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to deliver a message that must be delivered. + *
+ */ +public class NoRouteException extends RequiredDeliveryException +{ + public NoRouteException(String msg, AMQMessage amqMessage) + { + super(msg, amqMessage); + } + + public AMQConstant getReplyCode() + { + return AMQConstant.NO_ROUTE; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java new file mode 100644 index 0000000000..bc303a219d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -0,0 +1,670 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQShortStringTokenizer; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.topic.TopicParser; +import org.apache.qpid.server.exchange.topic.TopicMatcherResult; +import org.apache.qpid.server.filter.MessageFilter; +import org.apache.qpid.server.filter.JMSSelectorFilter; + +import javax.management.JMException; +import javax.management.MBeanException; +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.*; +import java.util.concurrent.ConcurrentHashMap; +import java.lang.ref.WeakReference; + +public class TopicExchange extends AbstractExchange +{ + + public static final ExchangeType TYPE = new ExchangeType() + { + + public AMQShortString getName() + { + return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; + } + + public Class getExchangeClass() + { + return TopicExchange.class; + } + + public TopicExchange newInstance(VirtualHost host, + AMQShortString name, + boolean durable, + int ticket, + boolean autoDelete) throws AMQException + { + TopicExchange exch = new TopicExchange(); + exch.initialise(host, name, durable, ticket, autoDelete); + return exch; + } + + public AMQShortString getDefaultExchangeName() + { + return ExchangeDefaults.TOPIC_EXCHANGE_NAME; + } + }; + + + private static final Logger _logger = Logger.getLogger(TopicExchange.class); + +/* + private final ConcurrentHashMap> _bindingKey2queues = + new ConcurrentHashMap>(); + private final ConcurrentHashMap> _simpleBindingKey2queues = + new ConcurrentHashMap>(); + private final ConcurrentHashMap> _wildCardBindingKey2queues = + new ConcurrentHashMap>(); +*/ + // private ConcurrentHashMap _routingKey2queue = new ConcurrentHashMap(); + private static final byte TOPIC_SEPARATOR = (byte)'.'; + private static final AMQShortString TOPIC_SEPARATOR_AS_SHORTSTRING = new AMQShortString("."); + private static final AMQShortString AMQP_STAR_TOKEN = new AMQShortString("*"); + private static final AMQShortString AMQP_HASH_TOKEN = new AMQShortString("#"); + + private static final byte HASH_BYTE = (byte)'#'; + private static final byte STAR_BYTE = (byte)'*'; + + private final TopicParser _parser = new TopicParser(); + + private final Map _topicExchangeResults = + new ConcurrentHashMap(); + + private final Map _bindings = new HashMap(); + + private final Map>> _selectorCache = new WeakHashMap>>(); + + public static class Binding + { + private final AMQShortString _bindingKey; + private final AMQQueue _queue; + private final FieldTable _args; + + public Binding(AMQShortString bindingKey, AMQQueue queue, FieldTable args) + { + _bindingKey = bindingKey; + _queue = queue; + _args = args; + } + + public AMQShortString getBindingKey() + { + return _bindingKey; + } + + public AMQQueue getQueue() + { + return _queue; + } + + public int hashCode() + { + return (_bindingKey == null ? 1 : _bindingKey.hashCode())*31 +_queue.hashCode(); + } + + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o instanceof Binding) + { + Binding other = (Binding) o; + return (_queue == other._queue) + && ((_bindingKey == null) ? other._bindingKey == null : _bindingKey.equals(other._bindingKey)); + } + return false; + } + } + + + + private final class TopicExchangeResult implements TopicMatcherResult + { + private final Map _unfilteredQueues = new ConcurrentHashMap(); + private final ConcurrentHashMap,Integer>> _filteredQueues = new ConcurrentHashMap, Integer>>(); + + public void addUnfilteredQueue(AMQQueue queue) + { + Integer instances = _unfilteredQueues.get(queue); + if(instances == null) + { + _unfilteredQueues.put(queue, 1); + } + else + { + _unfilteredQueues.put(queue, instances + 1); + } + } + + public void removeUnfilteredQueue(AMQQueue queue) + { + Integer instances = _unfilteredQueues.get(queue); + if(instances == 1) + { + _unfilteredQueues.remove(queue); + } + else + { + _unfilteredQueues.put(queue,instances - 1); + } + + } + + + public void addFilteredQueue(AMQQueue queue, MessageFilter filter) + { + Map,Integer> filters = _filteredQueues.get(queue); + if(filters == null) + { + filters = new ConcurrentHashMap,Integer>(); + _filteredQueues.put(queue, filters); + } + Integer instances = filters.get(filter); + if(instances == null) + { + filters.put(filter,1); + } + else + { + filters.put(filter, instances + 1); + } + + } + + public void removeFilteredQueue(AMQQueue queue, MessageFilter filter) + { + Map,Integer> filters = _filteredQueues.get(queue); + if(filters != null) + { + Integer instances = filters.get(filter); + if(instances != null) + { + if(instances == 1) + { + filters.remove(filter); + if(filters.isEmpty()) + { + _filteredQueues.remove(queue); + } + } + else + { + filters.put(filter, instances - 1); + } + } + + } + + } + + public void replaceQueueFilter(AMQQueue queue, + MessageFilter oldFilter, + MessageFilter newFilter) + { + Map,Integer> filters = _filteredQueues.get(queue); + Map,Integer> newFilters = new ConcurrentHashMap,Integer>(filters); + Integer oldFilterInstances = filters.get(oldFilter); + if(oldFilterInstances == 1) + { + newFilters.remove(oldFilter); + } + else + { + newFilters.put(oldFilter, oldFilterInstances-1); + } + Integer newFilterInstances = filters.get(newFilter); + if(newFilterInstances == null) + { + newFilters.put(newFilter, 1); + } + else + { + newFilters.put(newFilter, newFilterInstances+1); + } + _filteredQueues.put(queue,newFilters); + } + + public Collection processMessage(IncomingMessage msg, Collection queues) + { + if(queues == null) + { + if(_filteredQueues.isEmpty()) + { + return new ArrayList(_unfilteredQueues.keySet()); + } + else + { + queues = new HashSet(); + } + } + else if(!(queues instanceof Set)) + { + queues = new HashSet(queues); + } + + queues.addAll(_unfilteredQueues.keySet()); + if(!_filteredQueues.isEmpty()) + { + for(Map.Entry, Integer>> entry : _filteredQueues.entrySet()) + { + if(!queues.contains(entry.getKey())) + { + for(MessageFilter filter : entry.getValue().keySet()) + { + if(filter.matches(msg)) + { + queues.add(entry.getKey()); + } + } + } + } + } + return queues; + } + + } + + + /** TopicExchangeMBean class implements the management interface for the Topic exchanges. */ + @MBeanDescription("Management Bean for Topic Exchange") + private final class TopicExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ topic exchange") + public TopicExchangeMBean() throws JMException + { + super(); + _exchangeType = "topic"; + init(); + } + + /** returns exchange bindings in tabular form */ + public TabularData bindings() throws OpenDataException + { + _bindingList = new TabularDataSupport(_bindinglistDataType); + Map> bindingData = new HashMap>(); + for (Binding binding : _bindings.keySet()) + { + String key = binding.getBindingKey().toString(); + List queueNames = bindingData.get(key); + if(queueNames == null) + { + queueNames = new ArrayList(); + bindingData.put(key, queueNames); + } + queueNames.add(binding.getQueue().getName().toString()); + + } + for(Map.Entry> entry : bindingData.entrySet()) + { + Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]) }; + CompositeData bindingCompositeData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues); + _bindingList.put(bindingCompositeData); + } + + return _bindingList; + } + + public void createNewBinding(String queueName, String binding) throws JMException + { + AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (queue == null) + { + throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange."); + } + + try + { + queue.bind(TopicExchange.this, new AMQShortString(binding), null); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + } // End of MBean class + + public AMQShortString getType() + { + return ExchangeDefaults.TOPIC_EXCHANGE_CLASS; + } + + public synchronized void registerQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert rKey != null; + + _logger.debug("Registering queue " + queue.getName() + " with routing key " + rKey); + + + AMQShortString routingKey; + + if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE)) + { + routingKey = normalize(rKey); + } + else + { + routingKey = rKey; + } + + Binding binding = new Binding(rKey, queue, args); + + if(_bindings.containsKey(binding)) + { + FieldTable oldArgs = _bindings.get(binding); + TopicExchangeResult result = _topicExchangeResults.get(routingKey); + + if(argumentsContainSelector(args)) + { + if(argumentsContainSelector(oldArgs)) + { + result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args)); + } + else + { + result.addFilteredQueue(queue,createSelectorFilter(args)); + result.removeUnfilteredQueue(queue); + } + } + else + { + if(argumentsContainSelector(oldArgs)) + { + result.addUnfilteredQueue(queue); + result.removeFilteredQueue(queue, createSelectorFilter(oldArgs)); + } + else + { + // TODO - fix control flow + return; + } + } + + } + else + { + + TopicExchangeResult result = _topicExchangeResults.get(routingKey); + if(result == null) + { + result = new TopicExchangeResult(); + if(argumentsContainSelector(args)) + { + result.addFilteredQueue(queue, createSelectorFilter(args)); + } + else + { + result.addUnfilteredQueue(queue); + } + _parser.addBinding(routingKey, result); + _topicExchangeResults.put(routingKey,result); + } + else + { + if(argumentsContainSelector(args)) + { + result.addFilteredQueue(queue, createSelectorFilter(args)); + } + else + { + result.addUnfilteredQueue(queue); + } + } + _bindings.put(binding, args); + } + + + } + + private JMSSelectorFilter createSelectorFilter(final FieldTable args) + throws AMQException + { + + final String selectorString = args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); + WeakReference> selectorRef = _selectorCache.get(selectorString); + JMSSelectorFilter selector = null; + + if(selectorRef == null || (selector = selectorRef.get())==null) + { + selector = new JMSSelectorFilter(selectorString); + _selectorCache.put(selectorString, new WeakReference>(selector)); + } + return selector; + } + + private static boolean argumentsContainSelector(final FieldTable args) + { + return args != null && args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0; + } + + private AMQShortString normalize(AMQShortString routingKey) + { + if(routingKey == null) + { + routingKey = AMQShortString.EMPTY_STRING; + } + + AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR); + + List subscriptionList = new ArrayList(); + + while (routingTokens.hasMoreTokens()) + { + subscriptionList.add(routingTokens.nextToken()); + } + + int size = subscriptionList.size(); + + for (int index = 0; index < size; index++) + { + // if there are more levels + if ((index + 1) < size) + { + if (subscriptionList.get(index).equals(AMQP_HASH_TOKEN)) + { + if (subscriptionList.get(index + 1).equals(AMQP_HASH_TOKEN)) + { + // we don't need #.# delete this one + subscriptionList.remove(index); + size--; + // redo this normalisation + index--; + } + + if (subscriptionList.get(index + 1).equals(AMQP_STAR_TOKEN)) + { + // we don't want #.* swap to *.# + // remove it and put it in at index + 1 + subscriptionList.add(index + 1, subscriptionList.remove(index)); + } + } + } // if we have more levels + } + + + + AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING); + + return normalizedString; + } + + public void route(IncomingMessage payload) throws AMQException + { + + final AMQShortString routingKey = payload.getRoutingKey(); + + // The copy here is unfortunate, but not too bad relevant to the amount of + // things created and copied in getMatchedQueues + ArrayList queues = new ArrayList(); + queues.addAll(getMatchedQueues(payload, routingKey)); + + if(queues == null || queues.isEmpty()) + { + _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes."); + } + + payload.enqueue(queues); + + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + Binding binding = new Binding(routingKey, queue, arguments); + if (arguments == null) + { + return _bindings.containsKey(binding); + } + else + { + FieldTable o = _bindings.get(binding); + if (o != null) + { + return o.equals(arguments); + } + else + { + return false; + } + + } + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + return isBound(routingKey, null, queue); + } + + public boolean isBound(AMQShortString routingKey) + { + for(Binding b : _bindings.keySet()) + { + if(b.getBindingKey().equals(routingKey)) + { + return true; + } + } + + return false; + } + + public boolean isBound(AMQQueue queue) + { + for(Binding b : _bindings.keySet()) + { + if(b.getQueue().equals(queue)) + { + return true; + } + } + + return false; + } + + public boolean hasBindings() + { + return !_bindings.isEmpty(); + } + + public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert rKey != null; + + Binding binding = new Binding(rKey, queue, args); + + + if (!_bindings.containsKey(binding)) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue.getName() + " was not registered with exchange " + this.getName() + + " with routing key " + rKey + "."); + } + + FieldTable bindingArgs = _bindings.remove(binding); + AMQShortString bindingKey = normalize(rKey); + TopicExchangeResult result = _topicExchangeResults.get(bindingKey); + if(argumentsContainSelector(bindingArgs)) + { + result.removeFilteredQueue(queue, createSelectorFilter(bindingArgs)); + } + else + { + result.removeUnfilteredQueue(queue); + } + + } + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new TopicExchangeMBean(); + } + catch (JMException ex) + { + _logger.error("Exception occured in creating the topic exchenge mbean", ex); + throw new AMQException("Exception occured in creating the topic exchenge mbean", ex); + } + } + + private Collection getMatchedQueues(IncomingMessage message, AMQShortString routingKey) + { + + Collection results = _parser.parse(routingKey); + if(results.isEmpty()) + { + return Collections.EMPTY_SET; + } + else + { + Collection queues = results.size() == 1 ? null : new HashSet(); + for(TopicMatcherResult result : results) + { + + queues = ((TopicExchangeResult)result).processMessage(message, queues); + } + return queues; + } + + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java new file mode 100644 index 0000000000..8fdb91cbef --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java @@ -0,0 +1,40 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQShortString; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 HeaderKey +{ + public static final HeaderKey UNKNOWN = new HeaderKey(new AMQShortString("<< UNKNOWN >>")); + private AMQShortString _key; + + public HeaderKey(final AMQShortString key) + { + _key = key; + } + + public String toString() + { + return _key.toString(); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java new file mode 100644 index 0000000000..7be99a88c9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java @@ -0,0 +1,50 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.HashMap; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 HeaderKeyDictionary +{ + + private final Map _dictionary = new HashMap(); + + + public HeaderKey get(final AMQShortString key) + { + HeaderKey headerKey = _dictionary.get(key); + return headerKey == null ? HeaderKey.UNKNOWN : headerKey; + } + + public HeaderKey getOrCreate(final AMQShortString key) + { + HeaderKey headerKey = _dictionary.get(key); + if(headerKey == null) + { + headerKey = new HeaderKey(key); + _dictionary.put(key, headerKey); + } + return headerKey; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java new file mode 100644 index 0000000000..518064bb29 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.exchange.headers; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 HeaderMatcherResult +{ +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java new file mode 100644 index 0000000000..9da93d483a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java @@ -0,0 +1,339 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.AMQTypedValue; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.topic.TopicMatcherDFAState; +import org.apache.qpid.server.exchange.topic.TopicWord; +import org.apache.qpid.server.exchange.topic.TopicMatcherResult; + +import java.util.*; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 HeadersMatcherDFAState +{ + + + private final Collection _results; + private final Map> _nextStateMap; + private final HeaderKeyDictionary _dictionary; + + public HeadersMatcherDFAState(Map> nextStateMap, + Collection results, + HeaderKeyDictionary dictionary) + { + _nextStateMap = nextStateMap; + _results = results; + _dictionary = dictionary; + } + + + public Collection match(final FieldTable table) + { + return match(table.iterator()); + } + + + + public Collection match(Iterator> fieldTableIterator) + { + + if(_nextStateMap.isEmpty()) + { + return _results; + } + + while(fieldTableIterator.hasNext()) + { + + Map.Entry fieldTableEntry = fieldTableIterator.next(); + HeaderKey key = _dictionary.get(fieldTableEntry.getKey()); + if(key != HeaderKey.UNKNOWN) + { + Map valueToStateMap = _nextStateMap.get(key); + + if(valueToStateMap != null) + { + HeadersMatcherDFAState nextState = valueToStateMap.get(fieldTableEntry.getValue()); + + if(nextState == null) + { + nextState = valueToStateMap.get(null); + } + if(nextState != null && nextState != this) + { + return nextState.match(fieldTableIterator); + } + } + + } + } + + return _results; + + } + + + HeadersMatcherDFAState mergeStateMachines(HeadersMatcherDFAState otherStateMachine) + { + + assert(otherStateMachine._dictionary == _dictionary); + + Map, HeadersMatcherDFAState> newStateMap= new HashMap, HeadersMatcherDFAState>(); + + Collection results; + + if(_results.isEmpty()) + { + results = otherStateMachine._results; + } + else if(otherStateMachine._results.isEmpty()) + { + results = _results; + } + else + { + results = new HashSet(_results); + results.addAll(otherStateMachine._results); + } + + + final Map> newNextStateMap = new HashMap>(); + + HeadersMatcherDFAState newState = new HeadersMatcherDFAState(newNextStateMap, results, _dictionary); + + + Set oldStates = new HashSet(); + oldStates.add(this); + oldStates.add(otherStateMachine); + + newStateMap.put(oldStates, newState); + + mergeStateMachines(oldStates, newNextStateMap, newStateMap); + + return newState; + + + } + + private void mergeStateMachines(final Set oldStates, + final Map> newNextStateMap, + final Map, HeadersMatcherDFAState> newStateMap) + { + Map>> nfaMap = new HashMap>>(); + + Set distinctKeys = new HashSet(); + + for(HeadersMatcherDFAState state : oldStates) + { + Map> map = state._nextStateMap; + + for(Map.Entry> entry : map.entrySet()) + { + Map> valueToStatesMap = nfaMap.get(entry.getKey()); + + if(valueToStatesMap == null) + { + valueToStatesMap = new HashMap>(); + nfaMap.put(entry.getKey(), valueToStatesMap); + } + + for(Map.Entry valueToStateEntry : entry.getValue().entrySet()) + { + Set states = valueToStatesMap.get(valueToStateEntry.getKey()); + if(states == null) + { + states = new HashSet(); + valueToStatesMap.put(valueToStateEntry.getKey(),states); + } + states.add(valueToStateEntry.getValue()); + } + + distinctKeys.add(entry.getKey()); + } + } + + Map> anyValueStates = new HashMap>(); + + for(HeaderKey distinctKey : distinctKeys) + { + Map> valueToStateMap = nfaMap.get(distinctKey); + if(valueToStateMap != null) + { + Set statesForKeyDefault = valueToStateMap.get(null); + if(statesForKeyDefault != null) + { + anyValueStates.put(distinctKey, statesForKeyDefault); + } + } + } + + // add the defaults for "null" to all other specified values of a given header key + + for( Map.Entry>> entry : nfaMap.entrySet()) + { + Map> valueToStatesMap = entry.getValue(); + for(Map.Entry> valueToStates : valueToStatesMap.entrySet()) + { + if(valueToStates.getKey() != null) + { + + + Set defaults = anyValueStates.get(entry.getKey()); + if(defaults != null) + { + valueToStates.getValue().addAll(defaults); + } + } + } + } + + // if a given header key is not mentioned in the map of a machine; then that machine would stay at the same state + // for that key. + for(HeaderKey distinctKey : distinctKeys) + { + Map> valueToStatesMap = nfaMap.get(distinctKey); + for(HeadersMatcherDFAState oldState : oldStates) + { + if(!oldState._nextStateMap.containsKey(distinctKey)) + { + for(Set endStates : valueToStatesMap.values()) + { + endStates.add(oldState); + } + } + } + } + + + + + for(Map.Entry>> transitionClass : nfaMap.entrySet()) + { + Map valueToDFAState = newNextStateMap.get(transitionClass.getKey()); + if(valueToDFAState == null) + { + valueToDFAState = new HashMap(); + newNextStateMap.put(transitionClass.getKey(), valueToDFAState); + } + + for(Map.Entry> transition : transitionClass.getValue().entrySet()) + { + Set destinations = transition.getValue(); + + + HeadersMatcherDFAState nextState = newStateMap.get(destinations); + + if(nextState == null) + { + + if(destinations.size() == 1) + { + nextState = destinations.iterator().next(); + newStateMap.put(destinations, nextState); + } + else + { + Collection results; + + Set> resultSets = new HashSet>(); + for(HeadersMatcherDFAState destination : destinations) + { + resultSets.add(destination._results); + } + resultSets.remove(Collections.EMPTY_SET); + if(resultSets.size() == 0) + { + results = Collections.EMPTY_SET; + } + else if(resultSets.size() == 1) + { + results = resultSets.iterator().next(); + } + else + { + results = new HashSet(); + for(Collection oldResult : resultSets) + { + results.addAll(oldResult); + } + } + + final Map> nextStateMap = new HashMap>(); + + nextState = new HeadersMatcherDFAState(nextStateMap, results, _dictionary); + newStateMap.put(destinations, nextState); + + mergeStateMachines( + destinations, + nextStateMap, + newStateMap); + + + } + + + } + valueToDFAState.put(transition.getKey(),nextState); + } + } + + + + final ArrayList removeKeyList = new ArrayList(); + + for(Map.Entry> entry : _nextStateMap.entrySet()) + { + final ArrayList removeValueList = new ArrayList(); + + for(Map.Entry valueToDFAState : entry.getValue().entrySet()) + { + if(valueToDFAState.getValue() == this) + { + HeadersMatcherDFAState defaultState = entry.getValue().get(null); + if(defaultState == null || defaultState == this) + { + removeValueList.add(valueToDFAState.getKey()); + } + } + } + + for(AMQTypedValue removeValue : removeValueList) + { + entry.getValue().remove(removeValue); + } + + if(entry.getValue().isEmpty()) + { + removeKeyList.add(entry.getKey()); + } + + } + + for(HeaderKey removeKey : removeKeyList) + { + _nextStateMap.remove(removeKey); + } + + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java new file mode 100644 index 0000000000..85e74122c3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java @@ -0,0 +1,439 @@ +package org.apache.qpid.server.exchange.headers; + +import org.apache.qpid.framing.*; + +import java.util.*; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 HeadersParser +{ + + private final HeaderKeyDictionary _dictionary = new HeaderKeyDictionary(); + private static final AMQShortString MATCHING_TYPE_KEY = new AMQShortString("x-match"); + private static final String ANY_MATCHING = "any"; + private static final AMQShortString RESERVED_KEY_PREFIX = new AMQShortString("x-"); + + + HeadersMatcherDFAState createStateMachine(FieldTable bindingArguments, HeaderMatcherResult result) + { + String matchingType = bindingArguments.getString(MATCHING_TYPE_KEY); + boolean matchAny = matchingType.equalsIgnoreCase(ANY_MATCHING); + if(matchAny) + { + return createStateMachineForAnyMatch(bindingArguments, result); + } + else + { + return createStateMachineForAllMatch(bindingArguments, result); + } + + + } + + + private HeadersMatcherDFAState createStateMachineForAnyMatch(final FieldTable bindingArguments, + final HeaderMatcherResult result) + { + + // DFAs for "any" matches have only two states, "not-matched" and "matched"... they start in the former + // and upon meeting any of the criteria they move to the latter + + //noinspection unchecked + final HeadersMatcherDFAState successState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary); + + Map> nextStateMap = + new HashMap>(); + + Set seenKeys = new HashSet(); + + Iterator> tableIterator = bindingArguments.iterator(); + + while(tableIterator.hasNext()) + { + final Map.Entry entry = tableIterator.next(); + final AMQShortString key = entry.getKey(); + final AMQTypedValue value = entry.getValue(); + + + if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX)) + { + final AMQType type = value.getType(); + + final HeaderKey headerKey = _dictionary.getOrCreate(key); + final Map valueMap; + + if(type == AMQType.VOID || + ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0)) + { + valueMap = Collections.singletonMap(null,successState); + + } + else + { + valueMap = Collections.singletonMap(value,successState); + } + nextStateMap.put(headerKey,valueMap); + + } + + } + + if(seenKeys.size() == 0) + { + return successState; + } + else + { + return new HeadersMatcherDFAState(nextStateMap,Collections.EMPTY_SET,_dictionary); + } + + + } + + + private HeadersMatcherDFAState createStateMachineForAllMatch(final FieldTable bindingArguments, + final HeaderMatcherResult result) + { + // DFAs for "all" matches have a "success" state, a "fail" state, and states for every subset of + // matches which are possible, starting with the empty subset. For example if we have a binding + // { x-match="all" + // a=1 + // b=1 + // c=1 + // d=1 } + // Then we would have the following states + // (1) Seen none of a, b, c, or d + // (2) Seen a=1 ; none of b,c, or d + // (3) Seen b=1 ; none of a,c, or d + // (4) Seen c=1 ; none of a,b, or d + // (5) Seen d=1 ; none of a,b, or c + // (6) Seen a=1,b=1 ; none of c,d + // (7) Seen a=1,c=1 ; none of b,d + // (8) Seen a=1,d=1 ; none of b,c + // (9) Seen b=1,c=1 ; none of a,d + //(10) Seen b=1,d=1 ; none of c,d + //(11) Seen c=1,d=1 ; none of a,b + //(12) Seen a=1,b=1,c=1 ; not d + //(13) Seen a=1,b=1,d=1 ; not c + //(14) Seen a=1,c=1,d=1 ; not b + //(15) Seen b=1,c=1,d=1 ; not a + //(16) success + //(17) fail + // + // All states but (16) can transition to (17); additionally: + // (1) can transition to (2),(3),(4),(5) + // (2) can transition to (6),(7),(8) + // (3) can transition to (6),(9),(10) + // (4) can transition to (7),(9),(11) + // (5) can transition to (8),(10),(11) + // (6) can transition to (12),(13) + // (7) can transition to (12),(14) + // (8) can transition to (13),(14) + // (9) can transition to (12),(15) + //(10) can transition to (13),(15) + //(11) can transition to (14),(15) + //(12)-(15) can transition to (16) + + Set seenKeys = new HashSet(); + List requiredTerms = new ArrayList(bindingArguments.size()); + + Iterator> tableIterator = bindingArguments.iterator(); + + + + while(tableIterator.hasNext()) + { + final Map.Entry entry = tableIterator.next(); + final AMQShortString key = entry.getKey(); + final AMQTypedValue value = entry.getValue(); + + + if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX)) + { + final AMQType type = value.getType(); + + if(type == AMQType.VOID || + ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0)) + { + requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),null)); + } + else + { + requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),value)); + } + } + + } + + final HeadersMatcherDFAState successState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary); + + final HeadersMatcherDFAState failState = + new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.EMPTY_SET,_dictionary); + + Map, HeadersMatcherDFAState> notSeenTermsToStateMap = + new HashMap, HeadersMatcherDFAState>(); + + notSeenTermsToStateMap.put(Collections.EMPTY_SET, successState); + + + final int numberOfTerms = requiredTerms.size(); + + for(int numMissingTerms = 1; numMissingTerms <= numberOfTerms; numMissingTerms++) + { + int[] pos = new int[numMissingTerms]; + for(int i = 0; i < numMissingTerms; i++) + { + pos[i] = i; + } + + final int maxTermValue = (numberOfTerms - (numMissingTerms - 1)); + + while(pos[0] < maxTermValue) + { + + Set stateSet = new HashSet(); + for(int posIndex = 0; posIndex < pos.length; posIndex++) + { + stateSet.add(requiredTerms.get(pos[posIndex])); + } + + final Map> nextStateMap = + new HashMap>(); + + + for(int posIndex = 0; posIndex < pos.length; posIndex++) + { + KeyValuePair nextTerm = requiredTerms.get(pos[posIndex]); + HashSet nextStateSet = + new HashSet(stateSet); + nextStateSet.remove(nextTerm); + + Map valueToStateMap = + new HashMap(); + nextStateMap.put(nextTerm._key, valueToStateMap); + + valueToStateMap.put( nextTerm._value,notSeenTermsToStateMap.get(nextStateSet)); + if(nextTerm._value != null) + { + valueToStateMap.put(null, failState); + } + + + } + + + HeadersMatcherDFAState newState = new HeadersMatcherDFAState(nextStateMap, Collections.EMPTY_SET, _dictionary); + + notSeenTermsToStateMap.put(stateSet, newState); + + int i = numMissingTerms; + while(i-- != 0) + { + if(++pos[i] <= numberOfTerms -(numMissingTerms-i)) + { + int k = pos[i]; + for(int j = i+1; j < numMissingTerms; j++) + { + pos[j] = ++k; + } + break; + } + } + } + + + + + } + + + return notSeenTermsToStateMap.get(new HashSet(requiredTerms)); + + + + } + + public static void main(String[] args) throws AMQFrameDecodingException + { + + FieldTable bindingTable = new FieldTable(); + + bindingTable.setString(new AMQShortString("x-match"),"all"); + bindingTable.setInteger("a",1); + bindingTable.setVoid(new AMQShortString("b")); + bindingTable.setString("c",""); + bindingTable.setInteger("d",4); + bindingTable.setInteger("e",1); + + + + FieldTable bindingTable2 = new FieldTable(); + bindingTable2.setString(new AMQShortString("x-match"),"all"); + bindingTable2.setInteger("a",1); + bindingTable2.setVoid(new AMQShortString("b")); + bindingTable2.setString("c",""); + bindingTable2.setInteger("d",4); + bindingTable2.setInteger("e",1); + bindingTable2.setInteger("f",1); + + + FieldTable table = new FieldTable(); + table.setInteger("a",1); + table.setInteger("b",2); + table.setString("c",""); + table.setInteger("d",4); + table.setInteger("e",1); + table.setInteger("f",1); + table.setInteger("h",1); + table.setInteger("i",1); + table.setInteger("j",1); + table.setInteger("k",1); + table.setInteger("l",1); + + org.apache.mina.common.ByteBuffer buffer = org.apache.mina.common.ByteBuffer.allocate( (int) table.getEncodedSize()); + EncodingUtils.writeFieldTableBytes(buffer, table); + buffer.flip(); + + FieldTable table2 = EncodingUtils.readFieldTable(buffer); + + + + FieldTable bindingTable3 = new FieldTable(); + bindingTable3.setString(new AMQShortString("x-match"),"any"); + bindingTable3.setInteger("a",1); + bindingTable3.setInteger("b",3); + + + FieldTable bindingTable4 = new FieldTable(); + bindingTable4.setString(new AMQShortString("x-match"),"any"); + bindingTable4.setVoid(new AMQShortString("a")); + + + FieldTable bindingTable5 = new FieldTable(); + bindingTable5.setString(new AMQShortString("x-match"),"all"); + bindingTable5.setString(new AMQShortString("h"),"hello"); + + for(int i = 0; i < 100; i++) + { + printMatches(new FieldTable[] {bindingTable5} , table2); + } + + + + } + + + + private static void printMatches(final FieldTable[] bindingKeys, final FieldTable routingKey) + { + HeadersMatcherDFAState sm = null; + Map resultMap = new HashMap(); + + HeadersParser parser = new HeadersParser(); + + for(int i = 0; i < bindingKeys.length; i++) + { + HeaderMatcherResult r = new HeaderMatcherResult(); + resultMap.put(r, bindingKeys[i].toString()); + + + if(i==0) + { + sm = parser.createStateMachine(bindingKeys[i], r); + } + else + { + sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeys[i], r)); + } + } + + Collection results = null; + long beforeTime = System.currentTimeMillis(); + for(int i = 0; i < 1000000; i++) + { + routingKey.size(); + + assert sm != null; + results = sm.match(routingKey); + + } + long elapsed = System.currentTimeMillis() - beforeTime; + System.out.println("1000000 Iterations took: " + elapsed); + Collection resultStrings = new ArrayList(); + + assert results != null; + for(HeaderMatcherResult result : results) + { + resultStrings.add(resultMap.get(result)); + } + + final ArrayList nonMatches = new ArrayList(); + for(FieldTable key : bindingKeys) + { + nonMatches.add(key.toString()); + } + nonMatches.removeAll(resultStrings); + System.out.println("\""+routingKey+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches); + + + } + + + public final static class KeyValuePair + { + public final HeaderKey _key; + public final AMQTypedValue _value; + private final int _hashCode; + + public KeyValuePair(final HeaderKey key, final AMQTypedValue value) + { + _key = key; + _value = value; + int hash = (1 + 31 * _key.hashCode()); + if(_value != null) + { + hash+=_value.hashCode(); + } + _hashCode = hash; + } + + public int hashCode() + { + return _hashCode; + } + + public boolean equals(Object o) + { + KeyValuePair other = (KeyValuePair)o; + return (_key == other._key) && (_value == null ? other._value == null : _value.equals(other._value)); + } + + + public String toString() + { + return "{" + _key + " -> " + _value + "}"; + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java new file mode 100644 index 0000000000..36076cf75b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java @@ -0,0 +1,295 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQShortStringTokenizer; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 TopicMatcherDFAState +{ + private static final AtomicInteger stateId = new AtomicInteger(); + + private final int _id = stateId.incrementAndGet(); + + private final Collection _results; + private final Map _nextStateMap; + private static final byte TOPIC_DELIMITTER = (byte)'.'; + + + public TopicMatcherDFAState(Map nextStateMap, + Collection results ) + { + _nextStateMap = nextStateMap; + _results = results; + } + + + public TopicMatcherDFAState nextState(TopicWord word) + { + final TopicMatcherDFAState nextState = _nextStateMap.get(word); + return nextState == null ? _nextStateMap.get(TopicWord.ANY_WORD) : nextState; + } + + public Collection terminate() + { + return _results; + } + + + public Collection parse(TopicWordDictionary dictionary, AMQShortString routingKey) + { + return parse(dictionary, routingKey.tokenize(TOPIC_DELIMITTER)); + } + + private Collection parse(final TopicWordDictionary dictionary, + final AMQShortStringTokenizer tokens) + { + if(!tokens.hasMoreTokens()) + { + return _results; + } + TopicWord word = dictionary.getWord(tokens.nextToken()); + TopicMatcherDFAState nextState = _nextStateMap.get(word); + if(nextState == null && word != TopicWord.ANY_WORD) + { + nextState = _nextStateMap.get(TopicWord.ANY_WORD); + } + if(nextState == null) + { + return Collections.EMPTY_SET; + } + // Shortcut if we are at a looping terminal state + if((nextState == this) && (_nextStateMap.size() == 1) && _nextStateMap.containsKey(TopicWord.ANY_WORD)) + { + return _results; + } + + return nextState.parse(dictionary, tokens); + + } + + + public TopicMatcherDFAState mergeStateMachines(TopicMatcherDFAState otherStateMachine) + { + Map, TopicMatcherDFAState> newStateMap= new HashMap, TopicMatcherDFAState>(); + + Collection results; + + if(_results.isEmpty()) + { + results = otherStateMachine._results; + } + else if(otherStateMachine._results.isEmpty()) + { + results = _results; + } + else + { + results = new HashSet(_results); + results.addAll(otherStateMachine._results); + } + + + final Map newNextStateMap = new HashMap(); + + TopicMatcherDFAState newState = new TopicMatcherDFAState(newNextStateMap, results); + + + Set oldStates = new HashSet(); + oldStates.add(this); + oldStates.add(otherStateMachine); + + newStateMap.put(oldStates, newState); + + mergeStateMachines(oldStates, newNextStateMap, newStateMap); + + return newState; + + } + + private static void mergeStateMachines( + final Set oldStates, + final Map newNextStateMap, + final Map, TopicMatcherDFAState> newStateMap) + { + Map> nfaMap = new HashMap>(); + + for(TopicMatcherDFAState state : oldStates) + { + Map map = state._nextStateMap; + for(Map.Entry entry : map.entrySet()) + { + Set states = nfaMap.get(entry.getKey()); + if(states == null) + { + states = new HashSet(); + nfaMap.put(entry.getKey(), states); + } + states.add(entry.getValue()); + } + } + + Set anyWordStates = nfaMap.get(TopicWord.ANY_WORD); + + for(Map.Entry> transition : nfaMap.entrySet()) + { + Set destinations = transition.getValue(); + + if(anyWordStates != null) + { + destinations.addAll(anyWordStates); + } + + TopicMatcherDFAState nextState = newStateMap.get(destinations); + if(nextState == null) + { + + if(destinations.size() == 1) + { + nextState = destinations.iterator().next(); + newStateMap.put(destinations, nextState); + } + else + { + Collection results; + + Set> resultSets = new HashSet>(); + for(TopicMatcherDFAState destination : destinations) + { + resultSets.add(destination._results); + } + resultSets.remove(Collections.EMPTY_SET); + if(resultSets.size() == 0) + { + results = Collections.EMPTY_SET; + } + else if(resultSets.size() == 1) + { + results = resultSets.iterator().next(); + } + else + { + results = new HashSet(); + for(Collection oldResult : resultSets) + { + results.addAll(oldResult); + } + } + + final Map nextStateMap = new HashMap(); + + nextState = new TopicMatcherDFAState(nextStateMap, results); + newStateMap.put(destinations, nextState); + + mergeStateMachines( + destinations, + nextStateMap, + newStateMap); + + + } + + + } + newNextStateMap.put(transition.getKey(),nextState); + } + + // Remove redundant transitions where defined tokenWord has same action as ANY_WORD + TopicMatcherDFAState anyWordState = newNextStateMap.get(TopicWord.ANY_WORD); + if(anyWordState != null) + { + List removeList = new ArrayList(); + for(Map.Entry entry : newNextStateMap.entrySet()) + { + if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD) + { + removeList.add(entry.getKey()); + } + } + for(TopicWord removeKey : removeList) + { + newNextStateMap.remove(removeKey); + } + } + + + + } + + + public String toString() + { + StringBuilder transitions = new StringBuilder(); + for(Map.Entry entry : _nextStateMap.entrySet()) + { + transitions.append("[ "); + transitions.append(entry.getKey()); + transitions.append("\t ->\t "); + transitions.append(entry.getValue()._id); + transitions.append(" ]\n"); + } + + + return "[ State " + _id + " ]\n" + transitions + "\n"; + + } + + public String reachableStates() + { + StringBuilder result = new StringBuilder("Start state: " + _id + "\n"); + + SortedSet reachableStates = + new TreeSet(new Comparator() + { + public int compare(final TopicMatcherDFAState o1, final TopicMatcherDFAState o2) + { + return o1._id - o2._id; + } + }); + reachableStates.add(this); + + int count; + + do + { + count = reachableStates.size(); + Collection originalStates = new ArrayList(reachableStates); + for(TopicMatcherDFAState state : originalStates) + { + reachableStates.addAll(state._nextStateMap.values()); + } + } + while(reachableStates.size() != count); + + + + for(TopicMatcherDFAState state : reachableStates) + { + result.append(state.toString()); + } + + return result.toString(); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java new file mode 100644 index 0000000000..71d30adfac --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.exchange.topic; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 interface TopicMatcherResult +{ +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java new file mode 100644 index 0000000000..3e9facf412 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java @@ -0,0 +1,613 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQShortStringTokenizer; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.io.IOException; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 TopicParser +{ + private static final byte TOPIC_DELIMITER = (byte)'.'; + + private final TopicWordDictionary _dictionary = new TopicWordDictionary(); + private final AtomicReference _stateMachine = new AtomicReference(); + + private static class Position + { + private final TopicWord _word; + private final boolean _selfTransition; + private final int _position; + private final boolean _endState; + private boolean _followedByAnyLoop; + + + public Position(final int position, final TopicWord word, final boolean selfTransition, final boolean endState) + { + _position = position; + _word = word; + _selfTransition = selfTransition; + _endState = endState; + } + + + } + + private static final Position ERROR_POSITION = new Position(Integer.MAX_VALUE,null, true, false); + + private static class SimpleState + { + Set _positions; + Map _nextState; + } + + + public void addBinding(AMQShortString bindingKey, TopicMatcherResult result) + { + + TopicMatcherDFAState startingStateMachine; + TopicMatcherDFAState newStateMachine; + + do + { + startingStateMachine = _stateMachine.get(); + if(startingStateMachine == null) + { + newStateMachine = createStateMachine(bindingKey, result); + } + else + { + newStateMachine = startingStateMachine.mergeStateMachines(createStateMachine(bindingKey, result)); + } + + } + while(!_stateMachine.compareAndSet(startingStateMachine,newStateMachine)); + + } + + public Collection parse(AMQShortString routingKey) + { + TopicMatcherDFAState stateMachine = _stateMachine.get(); + if(stateMachine == null) + { + return Collections.EMPTY_SET; + } + else + { + return stateMachine.parse(_dictionary,routingKey); + } + } + + + TopicMatcherDFAState createStateMachine(AMQShortString bindingKey, TopicMatcherResult result) + { + List wordList = createTopicWordList(bindingKey); + int wildCards = 0; + for(TopicWord word : wordList) + { + if(word == TopicWord.WILDCARD_WORD) + { + wildCards++; + } + } + if(wildCards == 0) + { + TopicMatcherDFAState[] states = new TopicMatcherDFAState[wordList.size()+1]; + states[states.length-1] = new TopicMatcherDFAState(Collections.EMPTY_MAP, Collections.singleton(result)); + for(int i = states.length-2; i >= 0; i--) + { + states[i] = new TopicMatcherDFAState(Collections.singletonMap(wordList.get(i),states[i+1]),Collections.EMPTY_SET); + + } + return states[0]; + } + else if(wildCards == wordList.size()) + { + Map stateMap = new HashMap(); + TopicMatcherDFAState state = new TopicMatcherDFAState(stateMap, Collections.singleton(result)); + stateMap.put(TopicWord.ANY_WORD, state); + return state; + } + + + int positionCount = wordList.size() - wildCards; + + Position[] positions = new Position[positionCount+1]; + + int lastWord; + + if(wordList.get(wordList.size()-1)== TopicWord.WILDCARD_WORD) + { + lastWord = wordList.size()-1; + positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, true, true); + } + else + { + lastWord = wordList.size(); + positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, false, true); + } + + + int pos = 0; + int wordPos = 0; + + + while(wordPos < lastWord) + { + TopicWord word = wordList.get(wordPos++); + + if(word == TopicWord.WILDCARD_WORD) + { + int nextWordPos = wordPos++; + word = wordList.get(nextWordPos); + + positions[pos] = new Position(pos++,word,true,false); + } + else + { + positions[pos] = new Position(pos++,word,false,false); + } + + } + + + for(int p = 0; p,SimpleState> stateMap = new HashMap,SimpleState>(); + + + SimpleState state = new SimpleState(); + state._positions = Collections.singleton( positions[0] ); + stateMap.put(state._positions, state); + + calculateNextStates(state, stateMap, positions); + + SimpleState[] simpleStates = stateMap.values().toArray(new SimpleState[stateMap.size()]); + HashMap[] dfaStateMaps = new HashMap[simpleStates.length]; + Map simple2DFAMap = new HashMap(); + + for(int i = 0; i < simpleStates.length; i++) + { + + Collection results; + boolean endState = false; + + for(Position p : simpleStates[i]._positions) + { + if(p._endState) + { + endState = true; + break; + } + } + + if(endState) + { + results = Collections.singleton(result); + } + else + { + results = Collections.EMPTY_SET; + } + + dfaStateMaps[i] = new HashMap(); + simple2DFAMap.put(simpleStates[i], new TopicMatcherDFAState(dfaStateMaps[i],results)); + + } + for(int i = 0; i < simpleStates.length; i++) + { + SimpleState simpleState = simpleStates[i]; + + Map nextSimpleStateMap = simpleState._nextState; + for(Map.Entry stateMapEntry : nextSimpleStateMap.entrySet()) + { + dfaStateMaps[i].put(stateMapEntry.getKey(), simple2DFAMap.get(stateMapEntry.getValue())); + } + + } + + return simple2DFAMap.get(state); + + } + + + + private void calculateNextStates(final SimpleState state, + final Map, SimpleState> stateMap, + final Position[] positions) + { + Map> transitions = new HashMap>(); + + for(Position pos : state._positions) + { + if(pos._selfTransition) + { + Set dest = transitions.get(TopicWord.ANY_WORD); + if(dest == null) + { + dest = new HashSet(); + transitions.put(TopicWord.ANY_WORD,dest); + } + dest.add(pos); + } + + final int nextPos = pos._position + 1; + Position nextPosition = nextPos == positions.length ? ERROR_POSITION : positions[nextPos]; + + Set dest = transitions.get(pos._word); + if(dest == null) + { + dest = new HashSet(); + transitions.put(pos._word,dest); + } + dest.add(nextPosition); + + } + + Set anyWordTransitions = transitions.get(TopicWord.ANY_WORD); + if(anyWordTransitions != null) + { + for(Set dest : transitions.values()) + { + dest.addAll(anyWordTransitions); + } + } + + state._nextState = new HashMap(); + + for(Map.Entry> dest : transitions.entrySet()) + { + + if(dest.getValue().size()>1) + { + dest.getValue().remove(ERROR_POSITION); + } + Position loopingTerminal = null; + for(Position destPos : dest.getValue()) + { + if(destPos._selfTransition && destPos._endState) + { + loopingTerminal = destPos; + break; + } + } + + if(loopingTerminal!=null) + { + dest.setValue(Collections.singleton(loopingTerminal)); + } + else + { + Position anyLoop = null; + for(Position destPos : dest.getValue()) + { + if(destPos._followedByAnyLoop) + { + if(anyLoop == null || anyLoop._position removals = new ArrayList(); + for(Position destPos : dest.getValue()) + { + if(destPos._position < anyLoop._position) + { + removals.add(destPos); + } + } + dest.getValue().removeAll(removals); + } + } + + SimpleState stateForEntry = stateMap.get(dest.getValue()); + if(stateForEntry == null) + { + stateForEntry = new SimpleState(); + stateForEntry._positions = dest.getValue(); + stateMap.put(dest.getValue(),stateForEntry); + calculateNextStates(stateForEntry, + stateMap, + positions); + } + state._nextState.put(dest.getKey(),stateForEntry); + + + + } + + // remove redundant transitions + SimpleState anyWordState = state._nextState.get(TopicWord.ANY_WORD); + if(anyWordState != null) + { + List removeList = new ArrayList(); + for(Map.Entry entry : state._nextState.entrySet()) + { + if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD) + { + removeList.add(entry.getKey()); + } + } + for(TopicWord removeKey : removeList) + { + state._nextState.remove(removeKey); + } + } + + + } + + private List createTopicWordList(final AMQShortString bindingKey) + { + AMQShortStringTokenizer tokens = bindingKey.tokenize(TOPIC_DELIMITER); + TopicWord previousWord = null; + + List wordList = new ArrayList(); + + while(tokens.hasMoreTokens()) + { + TopicWord nextWord = _dictionary.getOrCreateWord(tokens.nextToken()); + if(previousWord == TopicWord.WILDCARD_WORD) + { + + if(nextWord == TopicWord.WILDCARD_WORD) + { + // consecutive wildcards can be merged + // i.e. subsequent wildcards can be discarded + continue; + } + else if(nextWord == TopicWord.ANY_WORD) + { + // wildcard and anyword can be reordered to always put anyword first + wordList.set(wordList.size()-1,TopicWord.ANY_WORD); + nextWord = TopicWord.WILDCARD_WORD; + } + } + wordList.add(nextWord); + previousWord = nextWord; + + } + return wordList; + } + + + public static void main(String[] args) + { + + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.*.q.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); + printMatches(new String[]{ + "#.a.#", + "#.b.#", + "#.c.#", + "#.d.#", + "#.e.#", + "#.f.#", + "#.g.#", + "#.h.#", + "#.i.#", + "#.j.#", + "#.k.#", + "#.l.#", + "#.m.#", + "#.n.#", + "#.o.#", + "#.p.#", + "#.q.#" + + }, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); +/* + printMatches(new String[]{ + "#.a.#", + "#.b.#", + "#.c.#", + "#.d.#", + "#.e.#", + "#.f.#", + "#.g.#", + "#.h.#", + "#.i.#", + "#.j.#", + "#.k.#", + "#.l.#", + "#.m.#", + "#.n.#", + "#.o.#", + "#.p.#", + "#.q.#", + "#.r.#", + "#.s.#", + "#.t.#", + "#.u.#", + "#.v.#", + "#.w.#", + "#.x.#", + "#.y.#", + "#.z.#" + + + },"a.b"); + + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); + printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z"); + printMatches("a.#.b.#","a.b.b.b.b.b.b.b.c"); + +*/ + + printMatches("",""); + printMatches("a","a"); + printMatches("a",""); + printMatches("","a"); + printMatches("a.b","a.b"); + printMatches("a","a.b"); + printMatches("a.b","a"); + printMatches("*","a"); + printMatches("*.b","a.b"); + printMatches("*.*","a.b"); + printMatches("a.*","a.b"); + printMatches("a.*.#","a.b"); + printMatches("a.#.b","a.b"); + + printMatches("#.b","a"); + printMatches("#.b","a.b"); + printMatches("#.a.b","a.b"); + + + printMatches("#",""); + printMatches("#","a"); + printMatches("#","a.b"); + printMatches("#.#","a.b"); + printMatches("#.*","a.b"); + + printMatches("#.a.b","a.b"); + printMatches("a.b.#","a.b"); + printMatches("a.#","a.b"); + printMatches("#.*.#","a.b"); + printMatches("#.*.b.#","a.b"); + printMatches("#.a.*.#","a.b"); + printMatches("#.a.#.b.#","a.b"); + printMatches("#.*.#.*.#","a.b"); + printMatches("*.#.*.#","a.b"); + printMatches("#.*.#.*","a.b"); + + + printMatches(new String[]{"a.#.b.#","a.*.#.b.#"},"a.b.b.b.b.b.b.b.c"); + + + printMatches(new String[]{"a.b", "a.c"},"a.b"); + printMatches(new String[]{"a.#", "a.c", "#.b"},"a.b"); + printMatches(new String[]{"a.#", "a.c", "#.b", "#", "*.*"},"a.b"); + + printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.e"); + printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.f.g"); + + + + + } + + private static void printMatches(final String[] bindingKeys, final String routingKey) + { + TopicMatcherDFAState sm = null; + Map resultMap = new HashMap(); + + TopicParser parser = new TopicParser(); + + long start = System.currentTimeMillis(); + for(int i = 0; i < bindingKeys.length; i++) + { + System.out.println((System.currentTimeMillis() - start) + ":\t" + bindingKeys[i]); + TopicMatcherResult r = new TopicMatcherResult(){}; + resultMap.put(r, bindingKeys[i]); + AMQShortString bindingKeyShortString = new AMQShortString(bindingKeys[i]); + + System.err.println("====================================================="); + System.err.println("Adding binding key: " + bindingKeyShortString); + System.err.println("-----------------------------------------------------"); + + + if(i==0) + { + sm = parser.createStateMachine(bindingKeyShortString, r); + } + else + { + sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeyShortString, r)); + } + System.err.println(sm.reachableStates()); + System.err.println("====================================================="); + try + { + System.in.read(); + } + catch (IOException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + AMQShortString routingKeyShortString = new AMQShortString(routingKey); + + Collection results = sm.parse(parser._dictionary, routingKeyShortString); + Collection resultStrings = new ArrayList(); + + for(TopicMatcherResult result : results) + { + resultStrings.add(resultMap.get(result)); + } + + final ArrayList nonMatches = new ArrayList(Arrays.asList(bindingKeys)); + nonMatches.removeAll(resultStrings); + System.out.println("\""+routingKeyShortString+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches); + + + } + + private static void printMatches(String bindingKey, String routingKey) + { + printMatches(new String[] { bindingKey }, routingKey); + } + + + private static boolean matches(String bindingKey, String routingKey) + { + AMQShortString bindingKeyShortString = new AMQShortString(bindingKey); + AMQShortString routingKeyShortString = new AMQShortString(routingKey); + TopicParser parser = new TopicParser(); + + final TopicMatcherResult result = new TopicMatcherResult(){}; + + TopicMatcherDFAState sm = parser.createStateMachine(bindingKeyShortString, result); + return !sm.parse(parser._dictionary,routingKeyShortString).isEmpty(); + + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java new file mode 100644 index 0000000000..f14d70f8a1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java @@ -0,0 +1,54 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 final class TopicWord +{ + public static final TopicWord ANY_WORD = new TopicWord("*"); + public static final TopicWord WILDCARD_WORD = new TopicWord("#"); + private String _word; + + public TopicWord() + { + + } + + public TopicWord(String s) + { + _word = s; + } + + public TopicWord(final AMQShortString name) + { + _word = name.toString(); + } + + public String toString() + { + return _word; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java new file mode 100644 index 0000000000..65a0cd3107 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java @@ -0,0 +1,63 @@ +package org.apache.qpid.server.exchange.topic; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.concurrent.ConcurrentHashMap; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 TopicWordDictionary +{ + private final ConcurrentHashMap _dictionary = + new ConcurrentHashMap(); + + + + public TopicWordDictionary() + { + _dictionary.put(new AMQShortString("*"), TopicWord.ANY_WORD); + _dictionary.put(new AMQShortString("#"), TopicWord.WILDCARD_WORD); + } + + + + + public TopicWord getOrCreateWord(AMQShortString name) + { + TopicWord word = _dictionary.putIfAbsent(name, new TopicWord(name)); + if(word == null) + { + word = _dictionary.get(name); + } + return word; + } + + + public TopicWord getWord(AMQShortString name) + { + TopicWord word = _dictionary.get(name); + if(word == null) + { + word = TopicWord.ANY_WORD; + } + return word; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java new file mode 100644 index 0000000000..a964bce306 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java @@ -0,0 +1,275 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.Filterable; + +/** + * An expression which performs an operation on two expression values + */ +public abstract class ArithmeticExpression extends BinaryExpression +{ + + protected static final int INTEGER = 1; + protected static final int LONG = 2; + protected static final int DOUBLE = 3; + + /** + * @param left + * @param right + */ + public ArithmeticExpression(Expression left, Expression right) + { + super(left, right); + } + + public static Expression createPlus(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof String) + { + String text = (String) lvalue; + String answer = text + rvalue; + + return answer; + } + else if (lvalue instanceof Number) + { + return plus((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call plus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "+"; + } + }; + } + + public static Expression createMinus(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return minus((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call minus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "-"; + } + }; + } + + public static Expression createMultiply(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return multiply((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call multiply operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "*"; + } + }; + } + + public static Expression createDivide(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return divide((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call divide operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "/"; + } + }; + } + + public static Expression createMod(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return mod((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call mod operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "%"; + } + }; + } + + protected Number plus(Number left, Number right) + { + switch (numberType(left, right)) + { + + case INTEGER: + return new Integer(left.intValue() + right.intValue()); + + case LONG: + return new Long(left.longValue() + right.longValue()); + + default: + return new Double(left.doubleValue() + right.doubleValue()); + } + } + + protected Number minus(Number left, Number right) + { + switch (numberType(left, right)) + { + + case INTEGER: + return new Integer(left.intValue() - right.intValue()); + + case LONG: + return new Long(left.longValue() - right.longValue()); + + default: + return new Double(left.doubleValue() - right.doubleValue()); + } + } + + protected Number multiply(Number left, Number right) + { + switch (numberType(left, right)) + { + + case INTEGER: + return new Integer(left.intValue() * right.intValue()); + + case LONG: + return new Long(left.longValue() * right.longValue()); + + default: + return new Double(left.doubleValue() * right.doubleValue()); + } + } + + protected Number divide(Number left, Number right) + { + return new Double(left.doubleValue() / right.doubleValue()); + } + + protected Number mod(Number left, Number right) + { + return new Double(left.doubleValue() % right.doubleValue()); + } + + private int numberType(Number left, Number right) + { + if (isDouble(left) || isDouble(right)) + { + return DOUBLE; + } + else if ((left instanceof Long) || (right instanceof Long)) + { + return LONG; + } + else + { + return INTEGER; + } + } + + private boolean isDouble(Number n) + { + return (n instanceof Float) || (n instanceof Double); + } + + protected Number asNumber(Object value) + { + if (value instanceof Number) + { + return (Number) value; + } + else + { + throw new RuntimeException("Cannot convert value: " + value + " into a number"); + } + } + + public Object evaluate(Filterable message) throws E + { + Object lvalue = left.evaluate(message); + if (lvalue == null) + { + return null; + } + + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + return evaluate(lvalue, rvalue); + } + + /** + * @param lvalue + * @param rvalue + * @return + */ + protected abstract Object evaluate(Object lvalue, Object rvalue); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java new file mode 100644 index 0000000000..7308de80d6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +/** + * An expression which performs an operation on two expression values. + */ +public abstract class BinaryExpression implements Expression +{ + protected Expression left; + protected Expression right; + + public BinaryExpression(Expression left, Expression right) + { + this.left = left; + this.right = right; + } + + public Expression getLeft() + { + return left; + } + + public Expression getRight() + { + return right; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + public abstract String getExpressionSymbol(); + + /** + * @param expression + */ + public void setRight(Expression expression) + { + right = expression; + } + + /** + * @param expression + */ + public void setLeft(Expression expression) + { + left = expression; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java new file mode 100644 index 0000000000..9beb9798d0 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java @@ -0,0 +1,41 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +/** + * A BooleanExpression is an expression that always + * produces a Boolean result. + */ +public interface BooleanExpression extends Expression +{ + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws E + */ + public boolean matches(Filterable message) throws E; + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java new file mode 100644 index 0000000000..921005c462 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java @@ -0,0 +1,601 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import java.util.HashSet; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +/** + * A filter performing a comparison of two objects + */ +public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression +{ + + public static BooleanExpression createBetween(Expression value, Expression left, Expression right) + { + return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right)); + } + + public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) + { + return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right)); + } + + private static final HashSet REGEXP_CONTROL_CHARS = new HashSet(); + + static + { + REGEXP_CONTROL_CHARS.add(new Character('.')); + REGEXP_CONTROL_CHARS.add(new Character('\\')); + REGEXP_CONTROL_CHARS.add(new Character('[')); + REGEXP_CONTROL_CHARS.add(new Character(']')); + REGEXP_CONTROL_CHARS.add(new Character('^')); + REGEXP_CONTROL_CHARS.add(new Character('$')); + REGEXP_CONTROL_CHARS.add(new Character('?')); + REGEXP_CONTROL_CHARS.add(new Character('*')); + REGEXP_CONTROL_CHARS.add(new Character('+')); + REGEXP_CONTROL_CHARS.add(new Character('{')); + REGEXP_CONTROL_CHARS.add(new Character('}')); + REGEXP_CONTROL_CHARS.add(new Character('|')); + REGEXP_CONTROL_CHARS.add(new Character('(')); + REGEXP_CONTROL_CHARS.add(new Character(')')); + REGEXP_CONTROL_CHARS.add(new Character(':')); + REGEXP_CONTROL_CHARS.add(new Character('&')); + REGEXP_CONTROL_CHARS.add(new Character('<')); + REGEXP_CONTROL_CHARS.add(new Character('>')); + REGEXP_CONTROL_CHARS.add(new Character('=')); + REGEXP_CONTROL_CHARS.add(new Character('!')); + } + + static class LikeExpression extends UnaryExpression implements BooleanExpression + { + + Pattern likePattern; + + /** + * @param right + */ + public LikeExpression(Expression right, String like, int escape) + { + super(right); + + StringBuffer regexp = new StringBuffer(like.length() * 2); + regexp.append("\\A"); // The beginning of the input + for (int i = 0; i < like.length(); i++) + { + char c = like.charAt(i); + if (escape == (0xFFFF & c)) + { + i++; + if (i >= like.length()) + { + // nothing left to escape... + break; + } + + char t = like.charAt(i); + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & t)); + } + else if (c == '%') + { + regexp.append(".*?"); // Do a non-greedy match + } + else if (c == '_') + { + regexp.append("."); // match one + } + else if (REGEXP_CONTROL_CHARS.contains(new Character(c))) + { + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & c)); + } + else + { + regexp.append(c); + } + } + + regexp.append("\\z"); // The end of the input + + likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL); + } + + /** + * org.apache.activemq.filter.UnaryExpression#getExpressionSymbol() + */ + public String getExpressionSymbol() + { + return "LIKE"; + } + + /** + * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext) + */ + public Object evaluate(Filterable message) throws E + { + + Object rv = this.getRight().evaluate(message); + + if (rv == null) + { + return null; + } + + if (!(rv instanceof String)) + { + return + Boolean.FALSE; + // throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass()); + } + + return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; + } + + public boolean matches(Filterable message) throws E + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + + public static BooleanExpression createLike(Expression left, String right, String escape) + { + if ((escape != null) && (escape.length() != 1)) + { + throw new RuntimeException( + "The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape); + } + + int c = -1; + if (escape != null) + { + c = 0xFFFF & escape.charAt(0); + } + + return new LikeExpression(left, right, c); + } + + public static BooleanExpression createNotLike(Expression left, String right, String escape) + { + return UnaryExpression.createNOT(createLike(left, right, escape)); + } + + public static BooleanExpression createInFilter(Expression left, List elements) + { + + if (!(left instanceof PropertyExpression)) + { + throw new RuntimeException("Expected a property for In expression, got: " + left); + } + + return UnaryExpression.createInExpression((PropertyExpression) left, elements, false); + + } + + public static BooleanExpression createNotInFilter(Expression left, List elements) + { + + if (!(left instanceof PropertyExpression)) + { + throw new RuntimeException("Expected a property for In expression, got: " + left); + } + + return UnaryExpression.createInExpression((PropertyExpression) left, elements, true); + + } + + public static BooleanExpression createIsNull(Expression left) + { + return doCreateEqual(left, ConstantExpression.NULL); + } + + public static BooleanExpression createIsNotNull(Expression left) + { + return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL)); + } + + public static BooleanExpression createNotEqual(Expression left, Expression right) + { + return UnaryExpression.createNOT(createEqual(left, right)); + } + + public static BooleanExpression createEqual(Expression left, Expression right) + { + checkEqualOperand(left); + checkEqualOperand(right); + checkEqualOperandCompatability(left, right); + + return doCreateEqual(left, right); + } + + private static BooleanExpression doCreateEqual(Expression left, Expression right) + { + return new EqualExpression(left, right); + } + + public static BooleanExpression createGreaterThan(final Expression left, final Expression right) + { + checkLessThanOperand(left); + checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + protected boolean asBoolean(int answer) + { + return answer > 0; + } + + public String getExpressionSymbol() + { + return ">"; + } + }; + } + + public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) + { + checkLessThanOperand(left); + checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + protected boolean asBoolean(int answer) + { + return answer >= 0; + } + + public String getExpressionSymbol() + { + return ">="; + } + }; + } + + public static BooleanExpression createLessThan(final Expression left, final Expression right) + { + checkLessThanOperand(left); + checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + + protected boolean asBoolean(int answer) + { + return answer < 0; + } + + public String getExpressionSymbol() + { + return "<"; + } + + }; + } + + public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) + { + checkLessThanOperand(left); + checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + + protected boolean asBoolean(int answer) + { + return answer <= 0; + } + + public String getExpressionSymbol() + { + return "<="; + } + }; + } + + /** + * Only Numeric expressions can be used in >, >=, < or <= expressions.s + * + * @param expr + */ + public static void checkLessThanOperand(Expression expr) + { + if (expr instanceof ConstantExpression) + { + Object value = ((ConstantExpression) expr).getValue(); + if (value instanceof Number) + { + return; + } + + // Else it's boolean or a String.. + throw new RuntimeException("Value '" + expr + "' cannot be compared."); + } + + if (expr instanceof BooleanExpression) + { + throw new RuntimeException("Value '" + expr + "' cannot be compared."); + } + } + + /** + * Validates that the expression can be used in == or <> expression. + * Cannot not be NULL TRUE or FALSE litterals. + * + * @param expr + */ + public static void checkEqualOperand(Expression expr) + { + if (expr instanceof ConstantExpression) + { + Object value = ((ConstantExpression) expr).getValue(); + if (value == null) + { + throw new RuntimeException("'" + expr + "' cannot be compared."); + } + } + } + + /** + * + * @param left + * @param right + */ + private static void checkEqualOperandCompatability(Expression left, Expression right) + { + if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression)) + { + if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression)) + { + throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'"); + } + } + } + + /** + * @param left + * @param right + */ + public ComparisonExpression(Expression left, Expression right) + { + super(left, right); + } + + public Object evaluate(Filterable message) throws E + { + Comparable lv = (Comparable) left.evaluate(message); + if (lv == null) + { + return null; + } + + Comparable rv = (Comparable) right.evaluate(message); + if (rv == null) + { + return null; + } + + return compare(lv, rv); + } + + protected Boolean compare(Comparable lv, Comparable rv) + { + Class lc = lv.getClass(); + Class rc = rv.getClass(); + // If the the objects are not of the same type, + // try to convert up to allow the comparison. + if (lc != rc) + { + if (lc == Byte.class) + { + if (rc == Short.class) + { + lv = new Short(((Number) lv).shortValue()); + } + else if (rc == Integer.class) + { + lv = new Integer(((Number) lv).intValue()); + } + else if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Short.class) + { + if (rc == Integer.class) + { + lv = new Integer(((Number) lv).intValue()); + } + else if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Integer.class) + { + if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Long.class) + { + if (rc == Integer.class) + { + rv = new Long(((Number) rv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Float.class) + { + if (rc == Integer.class) + { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Long.class) + { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Double.class) + { + if (rc == Integer.class) + { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Long.class) + { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Float.class) + { + rv = new Float(((Number) rv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else + { + return Boolean.FALSE; + } + } + + return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE; + } + + protected abstract boolean asBoolean(int answer); + + public boolean matches(Filterable message) throws E + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + + private static class EqualExpression extends ComparisonExpression + { + public EqualExpression(final Expression left, final Expression right) + { + super(left, right); + } + + public Object evaluate(Filterable message) throws E + { + Object lv = left.evaluate(message); + Object rv = right.evaluate(message); + + // Iff one of the values is null + if ((lv == null) ^ (rv == null)) + { + return Boolean.FALSE; + } + + if ((lv == rv) || lv.equals(rv)) + { + return Boolean.TRUE; + } + + if ((lv instanceof Comparable) && (rv instanceof Comparable)) + { + return compare((Comparable) lv, (Comparable) rv); + } + + return Boolean.FALSE; + } + + protected boolean asBoolean(int answer) + { + return answer == 0; + } + + public String getExpressionSymbol() + { + return "="; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java new file mode 100644 index 0000000000..3ed2286f2e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java @@ -0,0 +1,211 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import java.math.BigDecimal; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +/** + * Represents a constant expression + */ +public class ConstantExpression implements Expression +{ + + static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression + { + public BooleanConstantExpression(Object value) + { + super(value); + } + + public boolean matches(Filterable message) throws E + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + + public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null); + public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE); + public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE); + + private Object value; + + public static ConstantExpression createFromDecimal(String text) + { + + // Strip off the 'l' or 'L' if needed. + if (text.endsWith("l") || text.endsWith("L")) + { + text = text.substring(0, text.length() - 1); + } + + Number value; + try + { + value = new Long(text); + } + catch (NumberFormatException e) + { + // The number may be too big to fit in a long. + value = new BigDecimal(text); + } + + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFromHex(String text) + { + Number value = new Long(Long.parseLong(text.substring(2), 16)); + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFromOctal(String text) + { + Number value = new Long(Long.parseLong(text, 8)); + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFloat(String text) + { + Number value = new Double(text); + + return new ConstantExpression(value); + } + + public ConstantExpression(Object value) + { + this.value = value; + } + + public Object evaluate(Filterable message) throws E + { + return value; + } + + public Object getValue() + { + return value; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + if (value == null) + { + return "NULL"; + } + + if (value instanceof Boolean) + { + return ((Boolean) value).booleanValue() ? "TRUE" : "FALSE"; + } + + if (value instanceof String) + { + return encodeString((String) value); + } + + return value.toString(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Encodes the value of string so that it looks like it would look like + * when it was provided in a selector. + * + * @param s + * @return + */ + public static String encodeString(String s) + { + StringBuffer b = new StringBuffer(); + b.append('\''); + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + if (c == '\'') + { + b.append(c); + } + + b.append(c); + } + + b.append('\''); + + return b.toString(); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java new file mode 100644 index 0000000000..f2ebe41d26 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +/** + * Represents an expression + */ +public interface Expression +{ + + /** + * @return the value of this expression + */ + public Object evaluate(Filterable message) throws E; + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java new file mode 100644 index 0000000000..dd3c126ee5 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.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.server.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; +import org.apache.qpid.AMQException; + +public interface FilterManager +{ + void add(MessageFilter filter); + + void remove(MessageFilter filter); + + boolean allAllow(Filterable msg); + + boolean hasFilters(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java new file mode 100644 index 0000000000..a7f49d0566 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; + +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.FieldTable; + + +public class FilterManagerFactory +{ + //private final static Logger _logger = LoggerFactory.getLogger(FilterManagerFactory.class); + private final static org.apache.log4j.Logger _logger = org.apache.log4j.Logger.getLogger(FilterManagerFactory.class); + + //fixme move to a common class so it can be refered to from client code. + + public static FilterManager createManager(FieldTable filters) throws AMQException + { + FilterManager manager = null; + + if (filters != null) + { + + + + if(filters.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue())) + { + String selector = filters.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); + + if (selector != null && !selector.equals("")) + { + manager = new SimpleFilterManager(); + manager.add(new JMSSelectorFilter(selector)); + } + + } + + + } + else + { + _logger.debug("No Filters found."); + } + + + return manager; + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java new file mode 100644 index 0000000000..96c9353872 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.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.filter; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.filter.jms.selector.SelectorParser; +import org.apache.qpid.server.queue.Filterable; + + +public class JMSSelectorFilter implements MessageFilter +{ + private final static Logger _logger = org.apache.log4j.Logger.getLogger(JMSSelectorFilter.class); + + private String _selector; + private BooleanExpression _matcher; + + public JMSSelectorFilter(String selector) throws AMQException + { + _selector = selector; + _matcher = new SelectorParser().parse(selector); + } + + public boolean matches(Filterable message) throws E + { + boolean match = _matcher.matches(message); + if(_logger.isDebugEnabled()) + { + _logger.debug(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector); + } + return match; + } + + public String getSelector() + { + return _selector; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java new file mode 100644 index 0000000000..094363ed9a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java @@ -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. + */ +package org.apache.qpid.server.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +/** + * A filter performing a comparison of two objects + */ +public abstract class LogicExpression extends BinaryExpression implements BooleanExpression +{ + + public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) + { + return new OrExpression(lvalue, rvalue); + } + + public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) + { + return new AndExpression(lvalue, rvalue); + } + + /** + * @param left + * @param right + */ + public LogicExpression(BooleanExpression left, BooleanExpression right) + { + super(left, right); + } + + public abstract Object evaluate(Filterable message) throws E; + + public boolean matches(Filterable message) throws E + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + + private static class OrExpression extends LogicExpression + { + public OrExpression(final BooleanExpression lvalue, final BooleanExpression rvalue) + { + super(lvalue, rvalue); + } + + public Object evaluate(Filterable message) throws E + { + + Boolean lv = (Boolean) left.evaluate(message); + // Can we do an OR shortcut?? + if ((lv != null) && lv.booleanValue()) + { + return Boolean.TRUE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "OR"; + } + } + + private static class AndExpression extends LogicExpression + { + public AndExpression(final BooleanExpression lvalue, final BooleanExpression rvalue) + { + super(lvalue, rvalue); + } + + public Object evaluate(Filterable message) throws E + { + + Boolean lv = (Boolean) left.evaluate(message); + + // Can we do an AND shortcut?? + if (lv == null) + { + return null; + } + + if (!lv.booleanValue()) + { + return Boolean.FALSE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "AND"; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java new file mode 100644 index 0000000000..58fc55f8e6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.filter; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +public interface MessageFilter +{ + boolean matches(Filterable message) throws E; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java new file mode 100644 index 0000000000..f1b3b2511d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.filter; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.Filterable; + +public class NoConsumerFilter implements MessageFilter +{ + private final static Logger _logger = org.apache.log4j.Logger.getLogger(NoConsumerFilter.class); + + + public NoConsumerFilter() throws AMQException + { + _logger.info("Created NoConsumerFilter"); + } + + public boolean matches(Filterable message) + { + return true; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java new file mode 100644 index 0000000000..b30c70dac3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java @@ -0,0 +1,268 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import java.util.HashMap; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.CommonContentHeaderProperties; +import org.apache.qpid.server.queue.Filterable; + +/** + * Represents a property expression + */ +public class PropertyExpression implements Expression +{ + // Constants - defined the same as JMS + private static final int NON_PERSISTENT = 1; + private static final int PERSISTENT = 2; + private static final int DEFAULT_PRIORITY = 4; + + private static final Logger _logger = org.apache.log4j.Logger.getLogger(PropertyExpression.class); + + private static final HashMap> JMS_PROPERTY_EXPRESSIONS = new HashMap>(); + + { + JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression() + { + public Object evaluate(Filterable message) + { + //TODO + return null; + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new ReplyToExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new TypeExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new DeliveryModeExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new PriorityExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new MessageIDExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new TimestampExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new CorrelationIdExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new ExpirationExpression()); + + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression() + { + public Object evaluate(Filterable message) throws E + { + return message.isRedelivered(); + } + }); + } + + private final String name; + private final Expression jmsPropertyExpression; + + public boolean outerTest() + { + return false; + } + + public PropertyExpression(String name) + { + this.name = name; + + + + jmsPropertyExpression = (Expression) JMS_PROPERTY_EXPRESSIONS.get(name); + } + + public Object evaluate(Filterable message) throws E + { + + if (jmsPropertyExpression != null) + { + return jmsPropertyExpression.evaluate(message); + } + else + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) message.getContentHeaderBody().properties; + + if (_logger.isDebugEnabled()) + { + _logger.debug("Looking up property:" + name); + _logger.debug("Properties are:" + _properties.getHeaders().keySet()); + } + + return _properties.getHeaders().getObject(name); + } + } + + public String getName() + { + return name; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + return name; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return name.equals(((PropertyExpression) o).name); + + } + + private static class ReplyToExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString replyTo = _properties.getReplyTo(); + + return (replyTo == null) ? null : replyTo.toString(); + + } + + } + + private static class TypeExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString type = _properties.getType(); + + return (type == null) ? null : type.toString(); + + } + } + + private static class DeliveryModeExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT; + if (_logger.isDebugEnabled()) + { + _logger.debug("JMSDeliveryMode is :" + mode); + } + + return mode; + } + } + + private static class PriorityExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return (int) _properties.getPriority(); + } + } + + private static class MessageIDExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString messageId = _properties.getMessageId(); + + return (messageId == null) ? null : messageId; + + } + } + + private static class TimestampExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getTimestamp(); + } + } + + private static class CorrelationIdExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString correlationId = _properties.getCorrelationId(); + + return (correlationId == null) ? null : correlationId.toString(); + } + } + + private static class ExpirationExpression implements Expression + { + public Object evaluate(Filterable message) throws E + { + + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getExpiration(); + + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java new file mode 100644 index 0000000000..cb738e1489 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.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.filter; + +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +public class SimpleFilterManager implements FilterManager +{ + private final Logger _logger = Logger.getLogger(SimpleFilterManager.class); + + private final ConcurrentLinkedQueue> _filters; + + public SimpleFilterManager() + { + _logger.debug("Creating SimpleFilterManager"); + _filters = new ConcurrentLinkedQueue>(); + } + + public void add(MessageFilter filter) + { + _filters.add(filter); + } + + public void remove(MessageFilter filter) + { + _filters.remove(filter); + } + + public boolean allAllow(Filterable msg) + { + for (MessageFilter filter : _filters) + { + try + { + if (!filter.matches(msg)) + { + return false; + } + } + catch (AMQException e) + { + //fixme + e.printStackTrace(); + return false; + } + } + return true; + } + + public boolean hasFilters() + { + return !_filters.isEmpty(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java new file mode 100644 index 0000000000..799a38af5a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java @@ -0,0 +1,369 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.Filterable; + +/** + * An expression which performs an operation on two expression values + */ +public abstract class UnaryExpression implements Expression +{ + + private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE); + protected Expression right; + + public static Expression createNegate(Expression left) + { + return new NegativeExpression(left); + } + + public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not) + { + + // Use a HashSet if there are many elements. + Collection t; + if (elements.size() == 0) + { + t = null; + } + else if (elements.size() < 5) + { + t = elements; + } + else + { + t = new HashSet(elements); + } + + final Collection inList = t; + + return new InExpression(right, inList, not); + } + + abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression + { + public BooleanUnaryExpression(Expression left) + { + super(left); + } + + public boolean matches(Filterable message) throws E + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + ; + + public static BooleanExpression createNOT(BooleanExpression left) + { + return new NotExpression(left); + } + + public static BooleanExpression createXPath(final String xpath) + { + return new XPathExpression(xpath); + } + + public static BooleanExpression createXQuery(final String xpath) + { + return new XQueryExpression(xpath); + } + + public static BooleanExpression createBooleanCast(Expression left) + { + return new BooleanCastExpression(left); + } + + private static Number negate(Number left) + { + Class clazz = left.getClass(); + if (clazz == Integer.class) + { + return new Integer(-left.intValue()); + } + else if (clazz == Long.class) + { + return new Long(-left.longValue()); + } + else if (clazz == Float.class) + { + return new Float(-left.floatValue()); + } + else if (clazz == Double.class) + { + return new Double(-left.doubleValue()); + } + else if (clazz == BigDecimal.class) + { + // We ussually get a big deciamal when we have Long.MIN_VALUE constant in the + // Selector. Long.MIN_VALUE is too big to store in a Long as a positive so we store it + // as a Big decimal. But it gets Negated right away.. to here we try to covert it back + // to a Long. + BigDecimal bd = (BigDecimal) left; + bd = bd.negate(); + + if (BD_LONG_MIN_VALUE.compareTo(bd) == 0) + { + return new Long(Long.MIN_VALUE); + } + + return bd; + } + else + { + throw new RuntimeException("Don't know how to negate: " + left); + } + } + + public UnaryExpression(Expression left) + { + this.right = left; + } + + public Expression getRight() + { + return right; + } + + public void setRight(Expression expression) + { + right = expression; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + return "(" + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + public abstract String getExpressionSymbol(); + + private static class NegativeExpression extends UnaryExpression + { + public NegativeExpression(final Expression left) + { + super(left); + } + + public Object evaluate(Filterable message) throws E + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue instanceof Number) + { + return negate((Number) rvalue); + } + + return null; + } + + public String getExpressionSymbol() + { + return "-"; + } + } + + private static class InExpression extends BooleanUnaryExpression + { + private final Collection _inList; + private final boolean _not; + + public InExpression(final PropertyExpression right, final Collection inList, final boolean not) + { + super(right); + _inList = inList; + _not = not; + } + + public Object evaluate(Filterable message) throws E + { + + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue.getClass() != String.class) + { + return null; + } + + if (((_inList != null) && _inList.contains(rvalue)) ^ _not) + { + return Boolean.TRUE; + } + else + { + return Boolean.FALSE; + } + + } + + public String toString() + { + StringBuffer answer = new StringBuffer(); + answer.append(right); + answer.append(" "); + answer.append(getExpressionSymbol()); + answer.append(" ( "); + + int count = 0; + for (Iterator i = _inList.iterator(); i.hasNext();) + { + Object o = (Object) i.next(); + if (count != 0) + { + answer.append(", "); + } + + answer.append(o); + count++; + } + + answer.append(" )"); + + return answer.toString(); + } + + public String getExpressionSymbol() + { + if (_not) + { + return "NOT IN"; + } + else + { + return "IN"; + } + } + } + + private static class NotExpression extends BooleanUnaryExpression + { + public NotExpression(final BooleanExpression left) + { + super(left); + } + + public Object evaluate(Filterable message) throws E + { + Boolean lvalue = (Boolean) right.evaluate(message); + if (lvalue == null) + { + return null; + } + + return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + public String getExpressionSymbol() + { + return "NOT"; + } + } + + private static class BooleanCastExpression extends BooleanUnaryExpression + { + public BooleanCastExpression(final Expression left) + { + super(left); + } + + public Object evaluate(Filterable message) throws E + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (!rvalue.getClass().equals(Boolean.class)) + { + return Boolean.FALSE; + } + + return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE; + } + + public String toString() + { + return right.toString(); + } + + public String getExpressionSymbol() + { + return ""; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java new file mode 100644 index 0000000000..1311178fb1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java @@ -0,0 +1,127 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Used to evaluate an XPath Expression in a JMS selector. + */ +public final class XPathExpression implements BooleanExpression { + + private static final Logger log = Logger.getLogger(XPathExpression.class); + private static final String EVALUATOR_SYSTEM_PROPERTY = "org.apache.qpid.server.filter.XPathEvaluatorClassName"; + private static final String DEFAULT_EVALUATOR_CLASS_NAME=XalanXPathEvaluator.class.getName(); + + private static final Constructor EVALUATOR_CONSTRUCTOR; + + static { + String cn = System.getProperty(EVALUATOR_SYSTEM_PROPERTY, DEFAULT_EVALUATOR_CLASS_NAME); + Constructor m = null; + try { + try { + m = getXPathEvaluatorConstructor(cn); + } catch (Throwable e) { + log.warn("Invalid "+XPathEvaluator.class.getName()+" implementation: "+cn+", reason: "+e,e); + cn = DEFAULT_EVALUATOR_CLASS_NAME; + try { + m = getXPathEvaluatorConstructor(cn); + } catch (Throwable e2) { + log.error("Default XPath evaluator could not be loaded",e); + } + } + } finally { + EVALUATOR_CONSTRUCTOR = m; + } + } + + private static Constructor getXPathEvaluatorConstructor(String cn) throws ClassNotFoundException, SecurityException, NoSuchMethodException { + Class c = XPathExpression.class.getClassLoader().loadClass(cn); + if( !XPathEvaluator.class.isAssignableFrom(c) ) { + throw new ClassCastException(""+c+" is not an instance of "+XPathEvaluator.class); + } + return c.getConstructor(new Class[]{String.class}); + } + + private final String xpath; + private final XPathEvaluator evaluator; + + static public interface XPathEvaluator { + public boolean evaluate(Filterable message) throws AMQException; + } + + XPathExpression(String xpath) { + this.xpath = xpath; + this.evaluator = createEvaluator(xpath); + } + + private XPathEvaluator createEvaluator(String xpath2) { + try { + return (XPathEvaluator)EVALUATOR_CONSTRUCTOR.newInstance(new Object[]{xpath}); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if( cause instanceof RuntimeException ) { + throw (RuntimeException)cause; + } + throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e); + } catch (Throwable e) { + throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e); + } + } + + public Object evaluate(Filterable message) throws AMQException { +// try { +//FIXME this is flow to disk work +// if( message.isDropped() ) +// return null; + return evaluator.evaluate(message) ? Boolean.TRUE : Boolean.FALSE; +// } catch (IOException e) { +// +// JMSException exception = new JMSException(e.getMessage()); +// exception.initCause(e); +// throw exception; +// +// } + + } + + public String toString() { + return "XPATH "+ConstantExpression.encodeString(xpath); + } + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws AMQException + */ + public boolean matches(Filterable message) throws AMQException + { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java new file mode 100644 index 0000000000..c13f81cd08 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.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.filter; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; + +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +/** + * Used to evaluate an XQuery Expression in a JMS selector. + */ +public final class XQueryExpression implements BooleanExpression { + private final String xpath; + + XQueryExpression(String xpath) { + super(); + this.xpath = xpath; + } + + public Object evaluate(Filterable message) throws AMQException { + return Boolean.FALSE; + } + + public String toString() { + return "XQUERY "+ConstantExpression.encodeString(xpath); + } + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws AMQException + */ + public boolean matches(Filterable message) throws AMQException + { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java new file mode 100644 index 0000000000..cc67776682 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java @@ -0,0 +1,103 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import java.io.ByteArrayInputStream; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.Filterable; +import org.apache.xpath.CachedXPathAPI; +import org.w3c.dom.Document; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.InputSource; + +public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator { + + private final String xpath; + + public XalanXPathEvaluator(String xpath) { + this.xpath = xpath; + } + + public boolean evaluate(Filterable m) throws AMQException + { + // TODO - we would have to check the content type and then evaluate the content + // here... is this really a feature we wish to implement? - RobG + /* + + if( m instanceof TextMessage ) { + String text = ((TextMessage)m).getText(); + return evaluate(text); + } else if ( m instanceof BytesMessage ) { + BytesMessage bm = (BytesMessage) m; + byte data[] = new byte[(int) bm.getBodyLength()]; + bm.readBytes(data); + return evaluate(data); + } + */ + return false; + + } + + private boolean evaluate(byte[] data) { + try { + + InputSource inputSource = new InputSource(new ByteArrayInputStream(data)); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder dbuilder = factory.newDocumentBuilder(); + Document doc = dbuilder.parse(inputSource); + + CachedXPathAPI cachedXPathAPI = new CachedXPathAPI(); + NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath); + return iterator.nextNode()!=null; + + } catch (Throwable e) { + return false; + } + } + + private boolean evaluate(String text) { + try { + InputSource inputSource = new InputSource(new StringReader(text)); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder dbuilder = factory.newDocumentBuilder(); + Document doc = dbuilder.parse(inputSource); + + // We should associated the cachedXPathAPI object with the message being evaluated + // since that should speedup subsequent xpath expressions. + CachedXPathAPI cachedXPathAPI = new CachedXPathAPI(); + NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath); + return iterator.nextNode()!=null; + } catch (Throwable e) { + return false; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java new file mode 100644 index 0000000000..895db7b15b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java @@ -0,0 +1,62 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.flow; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Set; +import java.util.HashSet; + +public abstract class AbstractFlowCreditManager implements FlowCreditManager +{ + protected final AtomicBoolean _suspended = new AtomicBoolean(false); + private final Set _listeners = new HashSet(); + + public final void addStateListener(FlowCreditManagerListener listener) + { + _listeners.add(listener); + } + + public final boolean removeListener(FlowCreditManagerListener listener) + { + return _listeners.remove(listener); + } + + private void notifyListeners(final boolean suspended) + { + for(FlowCreditManagerListener listener : _listeners) + { + listener.creditStateChanged(!suspended); + } + } + + protected final void setSuspended(final boolean suspended) + { + if(_suspended.compareAndSet(!suspended, suspended)) + { + notifyListeners(suspended); + } + } + + protected final void notifyIncreaseBytesCredit() + { + notifyListeners(false); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java new file mode 100644 index 0000000000..96a1071135 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java @@ -0,0 +1,77 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.Set; +import java.util.HashSet; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 BytesOnlyCreditManager extends AbstractFlowCreditManager +{ + private final AtomicLong _bytesCredit; + + public BytesOnlyCreditManager(long initialCredit) + { + _bytesCredit = new AtomicLong(initialCredit); + } + + public void addCredit(long messageCredit, long bytesCredit) + { + _bytesCredit.addAndGet(bytesCredit); + setSuspended(false); + } + + public void removeAllCredit() + { + _bytesCredit.set(0L); + } + + public boolean hasCredit() + { + return _bytesCredit.get() > 0L; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + final long msgSize = msg.getSize(); + if(hasCredit()) + { + if(_bytesCredit.addAndGet(-msgSize) >= 0) + { + return true; + } + else + { + _bytesCredit.addAndGet(msgSize); + setSuspended(true); + return false; + } + } + else + { + return false; + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java new file mode 100644 index 0000000000..a249a6e63a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 interface FlowCreditManager +{ + + public static interface FlowCreditManagerListener + { + void creditStateChanged(boolean hasCredit); + } + + void addStateListener(FlowCreditManagerListener listener); + + boolean removeListener(FlowCreditManagerListener listener); + + public void addCredit(long messageCredit, long bytesCredit); + + public void removeAllCredit(); + + public boolean hasCredit(); + + public boolean useCreditForMessage(AMQMessage msg); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java new file mode 100644 index 0000000000..d63431c3eb --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 LimitlessCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + public void addCredit(long messageCredit, long bytesCredit) + { + } + + public void removeAllCredit() + { + } + + public boolean hasCredit() + { + return true; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + return true; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java new file mode 100644 index 0000000000..9c377481de --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java @@ -0,0 +1,79 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 MessageAndBytesCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + private long _messageCredit; + private long _bytesCredit; + + MessageAndBytesCreditManager(final long messageCredit, final long bytesCredit) + { + _messageCredit = messageCredit; + _bytesCredit = bytesCredit; + } + + public synchronized void addCredit(long messageCredit, long bytesCredit) + { + _messageCredit += messageCredit; + _bytesCredit += bytesCredit; + setSuspended(hasCredit()); + } + + public synchronized void removeAllCredit() + { + _messageCredit = 0L; + _bytesCredit = 0L; + setSuspended(true); + } + + public synchronized boolean hasCredit() + { + return (_messageCredit > 0L) && ( _bytesCredit > 0L ); + } + + public synchronized boolean useCreditForMessage(AMQMessage msg) + { + if(_messageCredit == 0L) + { + setSuspended(true); + return false; + } + else + { + final long msgSize = msg.getSize(); + if(msgSize > _bytesCredit) + { + setSuspended(true); + return false; + } + _messageCredit--; + _bytesCredit -= msgSize; + setSuspended(false); + return true; + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java new file mode 100644 index 0000000000..c1b3a09006 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java @@ -0,0 +1,76 @@ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.concurrent.atomic.AtomicLong; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 MessageOnlyCreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + private final AtomicLong _messageCredit; + + public MessageOnlyCreditManager(final long initialCredit) + { + _messageCredit = new AtomicLong(initialCredit); + } + + public void addCredit(long messageCredit, long bytesCredit) + { + setSuspended(false); + _messageCredit.addAndGet(messageCredit); + } + + public void removeAllCredit() + { + setSuspended(true); + _messageCredit.set(0L); + } + + public boolean hasCredit() + { + return _messageCredit.get() > 0L; + } + + public boolean useCreditForMessage(AMQMessage msg) + { + if(hasCredit()) + { + if(_messageCredit.addAndGet(-1L) >= 0) + { + setSuspended(false); + return true; + } + else + { + _messageCredit.addAndGet(1L); + setSuspended(true); + return false; + } + } + else + { + setSuspended(true); + return false; + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java new file mode 100644 index 0000000000..be0300f2c1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java @@ -0,0 +1,185 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.flow; + +import org.apache.qpid.server.queue.AMQMessage; + +public class Pre0_10CreditManager extends AbstractFlowCreditManager implements FlowCreditManager +{ + + private volatile long _bytesCreditLimit; + private volatile long _messageCreditLimit; + + private volatile long _bytesCredit; + private volatile long _messageCredit; + + public Pre0_10CreditManager(long bytesCreditLimit, long messageCreditLimit) + { + _bytesCreditLimit = bytesCreditLimit; + _messageCreditLimit = messageCreditLimit; + _bytesCredit = bytesCreditLimit; + _messageCredit = messageCreditLimit; + } + + + public synchronized void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit) + { + long bytesCreditChange = bytesCreditLimit - _bytesCreditLimit; + long messageCreditChange = messageCreditLimit - _messageCreditLimit; + + + + if(bytesCreditChange != 0L) + { + if(bytesCreditLimit == 0L) + { + _bytesCredit = 0; + } + else + { + _bytesCredit += bytesCreditChange; + } + } + + + if(messageCreditChange != 0L) + { + if(messageCreditLimit == 0L) + { + _messageCredit = 0; + } + else + { + _messageCredit += messageCreditChange; + } + } + + + _bytesCreditLimit = bytesCreditLimit; + _messageCreditLimit = messageCreditLimit; + + setSuspended(!hasCredit()); + + } + + + public synchronized void addCredit(final long messageCredit, final long bytesCredit) + { + final long messageCreditLimit = _messageCreditLimit; + boolean notifyIncrease = true; + if(messageCreditLimit != 0L) + { + notifyIncrease = (_messageCredit != 0); + long newCredit = _messageCredit + messageCredit; + _messageCredit = newCredit > messageCreditLimit ? messageCreditLimit : newCredit; + } + + + final long bytesCreditLimit = _bytesCreditLimit; + if(bytesCreditLimit != 0L) + { + long newCredit = _bytesCredit + bytesCredit; + _bytesCredit = newCredit > bytesCreditLimit ? bytesCreditLimit : newCredit; + if(notifyIncrease && bytesCredit>0) + { + notifyIncreaseBytesCredit(); + } + } + + + + setSuspended(!hasCredit()); + + } + + public synchronized void removeAllCredit() + { + _bytesCredit = 0L; + _messageCredit = 0L; + setSuspended(!hasCredit()); + } + + public synchronized boolean hasCredit() + { + return (_bytesCreditLimit == 0L || _bytesCredit > 0) + && (_messageCreditLimit == 0L || _messageCredit > 0); + } + + public synchronized boolean useCreditForMessage(final AMQMessage msg) + { + if(_messageCreditLimit != 0L) + { + if(_messageCredit != 0L) + { + if(_bytesCreditLimit == 0L) + { + _messageCredit--; + + return true; + } + else + { + if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + { + _messageCredit--; + _bytesCredit -= msg.getSize(); + + return true; + } + else + { + //setSuspended(true); + return false; + } + } + } + else + { + setSuspended(true); + return false; + } + } + else + { + if(_bytesCreditLimit == 0L) + { + + return true; + } + else + { + if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit)) + { + _bytesCredit -= msg.getSize(); + + return true; + } + else + { + //setSuspended(true); + return false; + } + } + + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java new file mode 100644 index 0000000000..e64eaeae76 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java @@ -0,0 +1,61 @@ +package org.apache.qpid.server.handler; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; + +/** + * @author Apache Software Foundation + * + * + */ +public class AccessRequestHandler implements StateAwareMethodListener +{ + private static final AccessRequestHandler _instance = new AccessRequestHandler(); + + + public static AccessRequestHandler getInstance() + { + return _instance; + } + + private AccessRequestHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AccessRequestBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + + // We don't implement access control class, but to keep clients happy that expect it + // always use the "0" ticket. + AccessRequestOkBody response = methodRegistry.createAccessRequestOkBody(0); + + session.writeFrame(response.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java new file mode 100644 index 0000000000..f90e7c3dff --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java @@ -0,0 +1,67 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicAckBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class BasicAckMethodHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(BasicAckMethodHandler.class); + + private static final BasicAckMethodHandler _instance = new BasicAckMethodHandler(); + + public static BasicAckMethodHandler getInstance() + { + return _instance; + } + + private BasicAckMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicAckBody body, int channelId) throws AMQException + { + AMQProtocolSession protocolSession = stateManager.getProtocolSession(); + + + if (_log.isDebugEnabled()) + { + _log.debug("Ack(Tag:" + body.getDeliveryTag() + ":Mult:" + body.getMultiple() + ") received on channel " + channelId); + } + + final AMQChannel channel = protocolSession.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + // this method throws an AMQException if the delivery tag is not known + channel.acknowledgeMessage(body.getDeliveryTag(), body.getMultiple()); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java new file mode 100644 index 0000000000..29054f55c1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.log4j.Logger; + +public class BasicCancelMethodHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(BasicCancelMethodHandler.class); + + private static final BasicCancelMethodHandler _instance = new BasicCancelMethodHandler(); + + public static BasicCancelMethodHandler getInstance() + { + return _instance; + } + + private BasicCancelMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicCancelBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + final AMQChannel channel = session.getChannel(channelId); + + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + if (_log.isDebugEnabled()) + { + _log.debug("BasicCancel: for:" + body.getConsumerTag() + + " nowait:" + body.getNowait()); + } + + channel.unsubscribeConsumer(body.getConsumerTag()); + if (!body.getNowait()) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + BasicCancelOkBody cancelOkBody = methodRegistry.createBasicCancelOkBody(body.getConsumerTag()); + session.writeFrame(cancelOkBody.generateFrame(channelId)); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java new file mode 100644 index 0000000000..5342a7f518 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java @@ -0,0 +1,168 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.ConsumerTagNotUniqueException; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class BasicConsumeMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicConsumeMethodHandler.class); + + private static final BasicConsumeMethodHandler _instance = new BasicConsumeMethodHandler(); + + public static BasicConsumeMethodHandler getInstance() + { + return _instance; + } + + private BasicConsumeMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicConsumeBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + + + AMQChannel channel = session.getChannel(channelId); + + VirtualHost vHost = session.getVirtualHost(); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("BasicConsume: from '" + body.getQueue() + + "' for:" + body.getConsumerTag() + + " nowait:" + body.getNowait() + + " args:" + body.getArguments()); + } + + AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue().intern()); + + if (queue == null) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("No queue for '" + body.getQueue() + "'"); + } + if (body.getQueue() != null) + { + String msg = "No such queue, '" + body.getQueue() + "'"; + throw body.getChannelException(AMQConstant.NOT_FOUND, msg); + } + else + { + String msg = "No queue name provided, no default queue defined."; + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, msg); + } + } + else + { + + final AMQShortString consumerTagName; + + //Perform ACLs + vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); + + if (body.getConsumerTag() != null) + { + consumerTagName = body.getConsumerTag().intern(); + } + else + { + consumerTagName = null; + } + + try + { + AMQShortString consumerTag = channel.subscribeToQueue(consumerTagName, queue, !body.getNoAck(), + body.getArguments(), body.getNoLocal(), body.getExclusive()); + if (!body.getNowait()) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createBasicConsumeOkBody(consumerTag); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + + + } + catch (org.apache.qpid.AMQInvalidArgumentException ise) + { + _logger.debug("Closing connection due to invalid selector"); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.INVALID_ARGUMENT.getCode(), + new AMQShortString(ise.getMessage()), + body.getClazz(), + body.getMethod()); + session.writeFrame(responseBody.generateFrame(channelId)); + + + } + catch (ConsumerTagNotUniqueException e) + { + AMQShortString msg = new AMQShortString("Non-unique consumer tag, '" + body.getConsumerTag() + "'"); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createConnectionCloseBody(AMQConstant.NOT_ALLOWED.getCode(), // replyCode + msg, // replytext + body.getClazz(), + body.getMethod()); + session.writeFrame(responseBody.generateFrame(0)); + } + catch (AMQQueue.ExistingExclusiveSubscription e) + { + throw body.getChannelException(AMQConstant.ACCESS_REFUSED, + "Cannot subscribe to queue " + + queue.getName() + + " as it already has an existing exclusive consumer"); + } + catch (AMQQueue.ExistingSubscriptionPreventsExclusive e) + { + throw body.getChannelException(AMQConstant.ACCESS_REFUSED, + "Cannot subscribe to queue " + + queue.getName() + + " exclusively as it already has a consumer"); + } + + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java new file mode 100644 index 0000000000..be1135dd91 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicGetBody; +import org.apache.qpid.framing.BasicGetEmptyBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.flow.MessageOnlyCreditManager; +import org.apache.qpid.server.subscription.SubscriptionImpl; +import org.apache.qpid.server.subscription.ClientDeliveryMethod; +import org.apache.qpid.server.subscription.RecordDeliveryMethod; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.SimpleAMQQueue; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class BasicGetMethodHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); + + private static final BasicGetMethodHandler _instance = new BasicGetMethodHandler(); + + public static BasicGetMethodHandler getInstance() + { + return _instance; + } + + private BasicGetMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicGetBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + VirtualHost vHost = session.getVirtualHost(); + + AMQChannel channel = session.getChannel(channelId); + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + else + { + AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue()); + if (queue == null) + { + _log.info("No queue for '" + body.getQueue() + "'"); + if(body.getQueue()!=null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, + "No such queue, '" + body.getQueue()+ "'"); + } + else + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "No queue name provided, no default queue defined."); + } + } + else + { + + //Perform ACLs + vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue); + + if (!performGet(queue,session, channel, !body.getNoAck())) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + // TODO - set clusterId + BasicGetEmptyBody responseBody = methodRegistry.createBasicGetEmptyBody(null); + + + session.writeFrame(responseBody.generateFrame(channelId)); + } + } + } + } + + public static boolean performGet(final AMQQueue queue, + final AMQProtocolSession session, + final AMQChannel channel, + final boolean acks) + throws AMQException + { + + final FlowCreditManager singleMessageCredit = new MessageOnlyCreditManager(1L); + + final ClientDeliveryMethod getDeliveryMethod = new ClientDeliveryMethod() + { + + int _msg; + + public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) + throws AMQException + { + singleMessageCredit.useCreditForMessage(entry.getMessage()); + session.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(), + deliveryTag, queue.getMessageCount()); + + } + }; + final RecordDeliveryMethod getRecordMethod = new RecordDeliveryMethod() + { + + public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag) + { + channel.addUnacknowledgedMessage(entry, deliveryTag, null); + } + }; + + Subscription sub; + if(acks) + { + sub = SubscriptionFactoryImpl.INSTANCE.createSubscription(channel, session, null, acks, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod); + } + else + { + sub = new GetNoAckSubscription(channel, + session, + null, + null, + false, + singleMessageCredit, + getDeliveryMethod, + getRecordMethod); + } + + queue.registerSubscription(sub,false); + queue.flushSubscription(sub); + queue.unregisterSubscription(sub); + return(!singleMessageCredit.hasCredit()); + + + } + + public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription + { + public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + } + + public boolean wouldSuspend(QueueEntry msg) + { + return !getCreditManager().useCreditForMessage(msg.getMessage()); + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java new file mode 100644 index 0000000000..e8e42454de --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java @@ -0,0 +1,101 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class BasicPublishMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicPublishMethodHandler.class); + + private static final BasicPublishMethodHandler _instance = new BasicPublishMethodHandler(); + + + public static BasicPublishMethodHandler getInstance() + { + return _instance; + } + + private BasicPublishMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicPublishBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Publish received on channel " + channelId); + } + + AMQShortString exchange = body.getExchange(); + // TODO: check the delivery tag field details - is it unique across the broker or per subscriber? + if (exchange == null) + { + exchange = ExchangeDefaults.DEFAULT_EXCHANGE_NAME; + + } + + VirtualHost vHost = session.getVirtualHost(); + Exchange e = vHost.getExchangeRegistry().getExchange(exchange); + // if the exchange does not exist we raise a channel exception + if (e == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange name"); + } + else + { + // The partially populated BasicDeliver frame plus the received route body + // is stored in the channel. Once the final body frame has been received + // it is routed to the exchange. + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //Access Control + vHost.getAccessManager().authorise(session, Permission.PUBLISH, body, e); + + MessagePublishInfo info = session.getMethodRegistry().getProtocolVersionMethodConverter().convertToInfo(body); + info.setExchange(exchange); + channel.setPublishFrame(info, e); + } + } + +} + + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java new file mode 100644 index 0000000000..dd3281c65f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicQosBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.AMQChannel; + +public class BasicQosHandler implements StateAwareMethodListener +{ + private static final BasicQosHandler _instance = new BasicQosHandler(); + + public static BasicQosHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, BasicQosBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + AMQChannel channel = session.getChannel(channelId); + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.setCredit(body.getPrefetchSize(), body.getPrefetchCount()); + + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createBasicQosOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java new file mode 100644 index 0000000000..c7842cd643 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.BasicRecoverOkBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class BasicRecoverMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicRecoverMethodHandler.class); + + private static final BasicRecoverMethodHandler _instance = new BasicRecoverMethodHandler(); + + public static BasicRecoverMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, BasicRecoverBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); + AMQChannel channel = session.getChannel(channelId); + + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.resend(body.getRequeue()); + + // Qpid 0-8 hacks a synchronous -ok onto recover. + // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant + if(session.getProtocolVersion().equals(ProtocolVersion.v8_0)) + { + MethodRegistry_8_0 methodRegistry = (MethodRegistry_8_0) session.getMethodRegistry(); + AMQMethodBody recoverOk = methodRegistry.createBasicRecoverOkBody(); + session.writeFrame(recoverOk.generateFrame(channelId)); + + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java new file mode 100644 index 0000000000..2c264c3d45 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverSyncMethodHandler.java @@ -0,0 +1,75 @@ +package org.apache.qpid.server.handler; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.log4j.Logger; + +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicRecoverSyncBody; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.AMQException; + +public class BasicRecoverSyncMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicRecoverSyncMethodHandler.class); + + private static final BasicRecoverSyncMethodHandler _instance = new BasicRecoverSyncMethodHandler(); + + public static BasicRecoverSyncMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, BasicRecoverSyncBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.debug("Recover received on protocol session " + session + " and channel " + channelId); + AMQChannel channel = session.getChannel(channelId); + + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.resend(body.getRequeue()); + + // Qpid 0-8 hacks a synchronous -ok onto recover. + // In Qpid 0-9 we create a separate sync-recover, sync-recover-ok pair to be "more" compliant + if(session.getProtocolVersion().equals(ProtocolVersion.v0_9)) + { + MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); + AMQMethodBody recoverOk = methodRegistry.createBasicRecoverSyncOkBody(); + session.writeFrame(recoverOk.generateFrame(channelId)); + + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java new file mode 100644 index 0000000000..f3cab10ed7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -0,0 +1,125 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicRejectBody; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.log4j.Logger; + +public class BasicRejectMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(BasicRejectMethodHandler.class); + + private static BasicRejectMethodHandler _instance = new BasicRejectMethodHandler(); + + public static BasicRejectMethodHandler getInstance() + { + return _instance; + } + + private BasicRejectMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, BasicRejectBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting:" + body.getDeliveryTag() + + ": Requeue:" + body.getRequeue() + + //": Resend:" + evt.getMethod().resend + + " on channel:" + channel.debugIdentity()); + } + + long deliveryTag = body.getDeliveryTag(); + + QueueEntry message = channel.getUnacknowledgedMessageMap().get(deliveryTag); + + if (message == null) + { + _logger.warn("Dropping reject request as message is null for tag:" + deliveryTag); +// throw evt.getMethod().getChannelException(AMQConstant.NOT_FOUND, "Delivery Tag(" + deliveryTag + ")not known"); + } + else + { + if (message.isQueueDeleted()) + { + _logger.warn("Message's Queue as already been purged, unable to Reject. " + + "Dropping message should use Dead Letter Queue"); + message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); + if(message != null) + { + message.discard(channel.getStoreContext()); + } + //sendtoDeadLetterQueue(msg) + return; + } + + if (!message.getMessage().isReferenced()) + { + _logger.warn("Message as already been purged, unable to Reject."); + return; + } + + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting: DT:" + deliveryTag + "-" + message.getMessage().debugIdentity() + + ": Requeue:" + body.getRequeue() + + //": Resend:" + evt.getMethod().resend + + " on channel:" + channel.debugIdentity()); + } + + // If we haven't requested message to be resent to this consumer then reject it from ever getting it. + //if (!evt.getMethod().resend) + { + message.reject(); + } + + if (body.getRequeue()) + { + channel.requeue(deliveryTag); + } + else + { + _logger.warn("Dropping message as requeue not required and there is no dead letter queue"); + message = channel.getUnacknowledgedMessageMap().remove(deliveryTag); + //sendtoDeadLetterQueue(AMQMessage message) +// message.queue = channel.getDefaultDeadLetterQueue(); +// channel.requeue(deliveryTag); + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java new file mode 100644 index 0000000000..9133cce6b7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.AMQChannel; + +public class ChannelCloseHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ChannelCloseHandler.class); + + private static ChannelCloseHandler _instance = new ChannelCloseHandler(); + + public static ChannelCloseHandler getInstance() + { + return _instance; + } + + private ChannelCloseHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ChannelCloseBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + if (_logger.isInfoEnabled()) + { + _logger.info("Received channel close for id " + channelId + " citing class " + body.getClassId() + + " and method " + body.getMethodId()); + } + + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getConnectionException(AMQConstant.CHANNEL_ERROR, "Trying to close unknown channel"); + } + + session.closeChannel(channelId); + // Client requested closure so we don't wait for ok we send it + stateManager.getProtocolSession().closeChannelOk(channelId); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + ChannelCloseOkBody responseBody = methodRegistry.createChannelCloseOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java new file mode 100644 index 0000000000..a857490e7e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ChannelCloseOkHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ChannelCloseOkHandler.class); + + private static ChannelCloseOkHandler _instance = new ChannelCloseOkHandler(); + + public static ChannelCloseOkHandler getInstance() + { + return _instance; + } + + private ChannelCloseOkHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ChannelCloseOkBody body, int channelId) throws AMQException + { + + _logger.info("Received channel-close-ok for channel-id " + channelId); + + // Let the Protocol Session know the channel is now closed. + stateManager.getProtocolSession().closeChannelOk(channelId); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java new file mode 100644 index 0000000000..696ca8a63b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ChannelFlowHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ChannelFlowHandler.class); + + private static ChannelFlowHandler _instance = new ChannelFlowHandler(); + + public static ChannelFlowHandler getInstance() + { + return _instance; + } + + private ChannelFlowHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ChannelFlowBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.setSuspended(!body.getActive()); + _logger.debug("Channel.Flow for channel " + channelId + ", active=" + body.getActive()); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createChannelFlowOkBody(body.getActive()); + session.writeFrame(responseBody.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java new file mode 100644 index 0000000000..054674aed4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java @@ -0,0 +1,103 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.util.UUID; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ChannelOpenHandler implements StateAwareMethodListener +{ + private static ChannelOpenHandler _instance = new ChannelOpenHandler(); + + public static ChannelOpenHandler getInstance() + { + return _instance; + } + + private ChannelOpenHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ChannelOpenBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + + final AMQChannel channel = new AMQChannel(session,channelId, virtualHost.getMessageStore() + ); + session.addChannel(channel); + + ChannelOpenOkBody response; + + ProtocolVersion pv = session.getProtocolVersion(); + + if(pv.equals(ProtocolVersion.v8_0)) + { + MethodRegistry_8_0 methodRegistry = (MethodRegistry_8_0) MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + response = methodRegistry.createChannelOpenOkBody(); + + } + else if(pv.equals(ProtocolVersion.v0_9)) + { + MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + UUID uuid = UUID.randomUUID(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + DataOutputStream dataOut = new DataOutputStream(output); + try + { + dataOut.writeLong(uuid.getMostSignificantBits()); + dataOut.writeLong(uuid.getLeastSignificantBits()); + dataOut.flush(); + dataOut.close(); + } + catch (IOException e) + { + // This *really* shouldn't happen as we're not doing any I/O + throw new RuntimeException("I/O exception when writing to byte array", e); + } + + // should really associate this channelId to the session + byte[] channelName = output.toByteArray(); + + response = methodRegistry.createChannelOpenOkBody(channelName); + } + else + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, "Got channel open for protocol version not catered for: " + pv, null); + } + + + session.writeFrame(response.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java new file mode 100644 index 0000000000..dade5d5f54 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ConnectionCloseMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionCloseMethodHandler.class); + + private static ConnectionCloseMethodHandler _instance = new ConnectionCloseMethodHandler(); + + public static ConnectionCloseMethodHandler getInstance() + { + return _instance; + } + + private ConnectionCloseMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ConnectionCloseBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + if (_logger.isInfoEnabled()) + { + _logger.info("ConnectionClose received with reply code/reply text " + body.getReplyCode() + "/" + + body.getReplyText() + " for " + session); + } + try + { + session.closeSession(); + } + catch (Exception e) + { + _logger.error("Error closing protocol session: " + e, e); + } + + MethodRegistry methodRegistry = session.getMethodRegistry(); + ConnectionCloseOkBody responseBody = methodRegistry.createConnectionCloseOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java new file mode 100644 index 0000000000..bc6e5ab403 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ConnectionCloseOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionCloseOkMethodHandler.class); + + private static ConnectionCloseOkMethodHandler _instance = new ConnectionCloseOkMethodHandler(); + + public static ConnectionCloseOkMethodHandler getInstance() + { + return _instance; + } + + private ConnectionCloseOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ConnectionCloseOkBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + //todo should this not do more than just log the method? + _logger.info("Received Connection-close-ok"); + + try + { + stateManager.changeState(AMQState.CONNECTION_CLOSED); + session.closeSession(); + } + catch (Exception e) + { + _logger.error("Error closing protocol session: " + e, e); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java new file mode 100644 index 0000000000..f99e650979 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java @@ -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. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.log4j.Logger; + +public class ConnectionOpenMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionOpenMethodHandler.class); + + private static ConnectionOpenMethodHandler _instance = new ConnectionOpenMethodHandler(); + + public static ConnectionOpenMethodHandler getInstance() + { + return _instance; + } + + private ConnectionOpenMethodHandler() + { + } + + private static AMQShortString generateClientID() + { + return new AMQShortString(Long.toString(System.currentTimeMillis())); + } + + public void methodReceived(AMQStateManager stateManager, ConnectionOpenBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + + //ignore leading '/' + String virtualHostName; + if ((body.getVirtualHost() != null) && body.getVirtualHost().charAt(0) == '/') + { + virtualHostName = new StringBuilder(body.getVirtualHost().subSequence(1, body.getVirtualHost().length())).toString(); + } + else + { + virtualHostName = body.getVirtualHost() == null ? null : String.valueOf(body.getVirtualHost()); + } + + VirtualHost virtualHost = stateManager.getVirtualHostRegistry().getVirtualHost(virtualHostName); + + if (virtualHost == null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, "Unknown virtual host: '" + virtualHostName + "'"); + } + else + { + session.setVirtualHost(virtualHost); + + //Perform ACL + virtualHost.getAccessManager().authorise(session, Permission.ACCESS ,body, virtualHost); + + // See Spec (0.8.2). Section 3.1.2 Virtual Hosts + if (session.getContextKey() == null) + { + session.setContextKey(generateClientID()); + } + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createConnectionOpenOkBody(body.getVirtualHost()); + + stateManager.changeState(AMQState.CONNECTION_OPEN); + + session.writeFrame(responseBody.generateFrame(channelId)); + + + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java new file mode 100644 index 0000000000..621003be90 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java @@ -0,0 +1,124 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.HeartbeatConfig; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionSecureOkMethodHandler.class); + + private static ConnectionSecureOkMethodHandler _instance = new ConnectionSecureOkMethodHandler(); + + public static ConnectionSecureOkMethodHandler getInstance() + { + return _instance; + } + + private ConnectionSecureOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ConnectionSecureOkBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager(); + + SaslServer ss = session.getSaslServer(); + if (ss == null) + { + throw new AMQException("No SASL context set up in session"); + } + MethodRegistry methodRegistry = session.getMethodRegistry(); + AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); + switch (authResult.status) + { + case ERROR: + Exception cause = authResult.getCause(); + + _logger.info("Authentication failed:" + (cause == null ? "" : cause.getMessage())); + + // This should be abstracted + stateManager.changeState(AMQState.CONNECTION_CLOSING); + + ConnectionCloseBody connectionCloseBody = + methodRegistry.createConnectionCloseBody(AMQConstant.NOT_ALLOWED.getCode(), + AMQConstant.NOT_ALLOWED.getName(), + body.getClazz(), + body.getMethod()); + + session.writeFrame(connectionCloseBody.generateFrame(0)); + disposeSaslServer(session); + break; + case SUCCESS: + _logger.info("Connected as: " + ss.getAuthorizationID()); + stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); + + ConnectionTuneBody tuneBody = + methodRegistry.createConnectionTuneBody(0xFFFF, + ConnectionStartOkMethodHandler.getConfiguredFrameSize(), + HeartbeatConfig.getInstance().getDelay()); + session.writeFrame(tuneBody.generateFrame(0)); + session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + disposeSaslServer(session); + break; + case CONTINUE: + stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); + + ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.challenge); + session.writeFrame(secureBody.generateFrame(0)); + } + } + + private void disposeSaslServer(AMQProtocolSession ps) + { + SaslServer ss = ps.getSaslServer(); + if (ss != null) + { + ps.setSaslServer(null); + try + { + ss.dispose(); + } + catch (SaslException e) + { + _logger.error("Error disposing of Sasl server: " + e); + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java new file mode 100644 index 0000000000..f53e56601b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.HeartbeatConfig; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + + +public class ConnectionStartOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionStartOkMethodHandler.class); + + private static ConnectionStartOkMethodHandler _instance = new ConnectionStartOkMethodHandler(); + + private static final int DEFAULT_FRAME_SIZE = 65536; + + public static ConnectionStartOkMethodHandler getInstance() + { + return _instance; + } + + private ConnectionStartOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ConnectionStartOkBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.info("SASL Mechanism selected: " + body.getMechanism()); + _logger.info("Locale selected: " + body.getLocale()); + + AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();//session.getVirtualHost().getAuthenticationManager(); + + SaslServer ss = null; + try + { + ss = authMgr.createSaslServer(String.valueOf(body.getMechanism()), session.getLocalFQDN()); + + if (ss == null) + { + throw body.getConnectionException(AMQConstant.RESOURCE_ERROR, "Unable to create SASL Server:" + body.getMechanism() + ); + } + + session.setSaslServer(ss); + + AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); + + //save clientProperties + if (session.getClientProperties() == null) + { + session.setClientProperties(body.getClientProperties()); + } + + MethodRegistry methodRegistry = session.getMethodRegistry(); + + switch (authResult.status) + { + case ERROR: + Exception cause = authResult.getCause(); + + _logger.info("Authentication failed:" + (cause == null ? "" : cause.getMessage())); + + stateManager.changeState(AMQState.CONNECTION_CLOSING); + + ConnectionCloseBody closeBody = + methodRegistry.createConnectionCloseBody(AMQConstant.NOT_ALLOWED.getCode(), // replyCode + AMQConstant.NOT_ALLOWED.getName(), + body.getClazz(), + body.getMethod()); + + session.writeFrame(closeBody.generateFrame(0)); + disposeSaslServer(session); + break; + + case SUCCESS: + _logger.info("Connected as: " + ss.getAuthorizationID()); + session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + + stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); + + ConnectionTuneBody tuneBody = methodRegistry.createConnectionTuneBody(0xFFFF, + getConfiguredFrameSize(), + HeartbeatConfig.getInstance().getDelay()); + session.writeFrame(tuneBody.generateFrame(0)); + break; + case CONTINUE: + stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); + + ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.challenge); + session.writeFrame(secureBody.generateFrame(0)); + } + } + catch (SaslException e) + { + disposeSaslServer(session); + throw new AMQException("SASL error: " + e, e); + } + } + + private void disposeSaslServer(AMQProtocolSession ps) + { + SaslServer ss = ps.getSaslServer(); + if (ss != null) + { + ps.setSaslServer(null); + try + { + ss.dispose(); + } + catch (SaslException e) + { + _logger.error("Error disposing of Sasl server: " + e); + } + } + } + + static int getConfiguredFrameSize() + { + final Configuration config = ApplicationRegistry.getInstance().getConfiguration(); + final int framesize = config.getInt("advanced.framesize", DEFAULT_FRAME_SIZE); + _logger.info("Framesize set to " + framesize); + return framesize; + } +} + + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java new file mode 100644 index 0000000000..0fe8c5dc92 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ConnectionTuneOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ConnectionTuneOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ConnectionTuneOkMethodHandler.class); + + private static ConnectionTuneOkMethodHandler _instance = new ConnectionTuneOkMethodHandler(); + + public static ConnectionTuneOkMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, ConnectionTuneOkBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + if (_logger.isDebugEnabled()) + { + _logger.debug(body); + } + stateManager.changeState(AMQState.CONNECTION_NOT_OPENED); + session.initHeartbeats(body.getHeartbeat()); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java new file mode 100644 index 0000000000..ccd42204d9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java @@ -0,0 +1,178 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * @author Apache Software Foundation + * + * + */ +public class ExchangeBoundHandler implements StateAwareMethodListener +{ + private static final ExchangeBoundHandler _instance = new ExchangeBoundHandler(); + + public static final int OK = 0; + + public static final int EXCHANGE_NOT_FOUND = 1; + + public static final int QUEUE_NOT_FOUND = 2; + + public static final int NO_BINDINGS = 3; + + public static final int QUEUE_NOT_BOUND = 4; + + public static final int NO_QUEUE_BOUND_WITH_RK = 5; + + public static final int SPECIFIC_QUEUE_NOT_BOUND_WITH_RK = 6; + + public static ExchangeBoundHandler getInstance() + { + return _instance; + } + + private ExchangeBoundHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ExchangeBoundBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MethodRegistry methodRegistry = session.getMethodRegistry(); + + + + + AMQShortString exchangeName = body.getExchange(); + AMQShortString queueName = body.getQueue(); + AMQShortString routingKey = body.getRoutingKey(); + if (exchangeName == null) + { + throw new AMQException("Exchange exchange must not be null"); + } + Exchange exchange = virtualHost.getExchangeRegistry().getExchange(exchangeName); + ExchangeBoundOkBody response; + if (exchange == null) + { + + + response = methodRegistry.createExchangeBoundOkBody(EXCHANGE_NOT_FOUND, + new AMQShortString("Exchange " + exchangeName + " not found")); + } + else if (routingKey == null) + { + if (queueName == null) + { + if (exchange.hasBindings()) + { + response = methodRegistry.createExchangeBoundOkBody(OK, null); + } + else + { + + response = methodRegistry.createExchangeBoundOkBody(NO_BINDINGS, // replyCode + null); // replyText + } + } + else + { + + AMQQueue queue = queueRegistry.getQueue(queueName); + if (queue == null) + { + + response = methodRegistry.createExchangeBoundOkBody(QUEUE_NOT_FOUND, // replyCode + new AMQShortString("Queue " + queueName + " not found")); // replyText + } + else + { + if (exchange.isBound(queue)) + { + + response = methodRegistry.createExchangeBoundOkBody(OK, // replyCode + null); // replyText + } + else + { + + response = methodRegistry.createExchangeBoundOkBody(QUEUE_NOT_BOUND, // replyCode + new AMQShortString("Queue " + queueName + " not bound to exchange " + exchangeName)); // replyText + } + } + } + } + else if (queueName != null) + { + AMQQueue queue = queueRegistry.getQueue(queueName); + if (queue == null) + { + + response = methodRegistry.createExchangeBoundOkBody(QUEUE_NOT_FOUND, // replyCode + new AMQShortString("Queue " + queueName + " not found")); // replyText + } + else + { + if (exchange.isBound(body.getRoutingKey(), queue)) + { + + response = methodRegistry.createExchangeBoundOkBody(OK, // replyCode + null); // replyText + } + else + { + + response = methodRegistry.createExchangeBoundOkBody(SPECIFIC_QUEUE_NOT_BOUND_WITH_RK, // replyCode + new AMQShortString("Queue " + queueName + " not bound with routing key " + + body.getRoutingKey() + " to exchange " + exchangeName)); // replyText + } + } + } + else + { + if (exchange.isBound(body.getRoutingKey())) + { + + response = methodRegistry.createExchangeBoundOkBody(OK, // replyCode + null); // replyText + } + else + { + + response = methodRegistry.createExchangeBoundOkBody(NO_QUEUE_BOUND_WITH_RK, // replyCode + new AMQShortString("No queue bound with routing key " + body.getRoutingKey() + + " to exchange " + exchangeName)); // replyText + } + } + session.writeFrame(response.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java new file mode 100644 index 0000000000..39b048aecb --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java @@ -0,0 +1,116 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +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.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ExchangeDeclareHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ExchangeDeclareHandler.class); + + private static final ExchangeDeclareHandler _instance = new ExchangeDeclareHandler(); + + public static ExchangeDeclareHandler getInstance() + { + return _instance; + } + + + + private ExchangeDeclareHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ExchangeDeclareBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + + if (!body.getPassive()) + { + //Perform ACL if request is not passive + virtualHost.getAccessManager().authorise(session, Permission.CREATE, body); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Request to declare exchange of type " + body.getType() + " with name " + body.getExchange()); + } + synchronized(exchangeRegistry) + { + Exchange exchange = exchangeRegistry.getExchange(body.getExchange()); + + + + if (exchange == null) + { + if(body.getPassive() && ((body.getType() == null) || body.getType().length() ==0)) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange: " + body.getExchange()); + } + else + { + try + { + + exchange = exchangeFactory.createExchange(body.getExchange() == null ? null : body.getExchange().intern(), + body.getType() == null ? null : body.getType().intern(), + body.getDurable(), + body.getPassive(), body.getTicket()); + exchangeRegistry.registerExchange(exchange); + } + catch(AMQUnknownExchangeType e) + { + throw body.getConnectionException(AMQConstant.COMMAND_INVALID, "Unknown exchange: " + body.getExchange(),e); + } + } + } + else if (!exchange.getType().equals(body.getType())) + { + + throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.getExchange() + " of type " + exchange.getType() + " to " + body.getType() +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor(),null); + } + + } + if(!body.getNowait()) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createExchangeDeclareOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java new file mode 100644 index 0000000000..888ffcb2e5 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ExchangeDeleteBody; +import org.apache.qpid.framing.ExchangeDeleteOkBody; +import org.apache.qpid.server.exchange.ExchangeInUseException; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ExchangeDeleteHandler implements StateAwareMethodListener +{ + private static final ExchangeDeleteHandler _instance = new ExchangeDeleteHandler(); + + public static ExchangeDeleteHandler getInstance() + { + return _instance; + } + + private ExchangeDeleteHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, ExchangeDeleteBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.DELETE,body, + exchangeRegistry.getExchange(body.getExchange())); + + try + { + exchangeRegistry.unregisterExchange(body.getExchange(), body.getIfUnused()); + + ExchangeDeleteOkBody responseBody = session.getMethodRegistry().createExchangeDeleteOkBody(); + + session.writeFrame(responseBody.generateFrame(channelId)); + } + catch (ExchangeInUseException e) + { + // TODO: sort out consistent channel close mechanism that does all clean up etc. + } + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java new file mode 100644 index 0000000000..ac516b6133 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.util.concurrent.Executor; + +/** + * An executor that executes the task on the current thread. + */ +public class OnCurrentThreadExecutor implements Executor +{ + public void execute(Runnable command) + { + command.run(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java new file mode 100644 index 0000000000..46182e8c00 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java @@ -0,0 +1,139 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class QueueBindHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(QueueBindHandler.class); + + private static final QueueBindHandler _instance = new QueueBindHandler(); + + public static QueueBindHandler getInstance() + { + return _instance; + } + + private QueueBindHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, QueueBindBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + + final AMQQueue queue; + final AMQShortString routingKey; + + if (body.getQueue() == null) + { + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + queue = channel.getDefaultQueue(); + + if (queue == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null"); + } + + if (body.getRoutingKey() == null) + { + routingKey = queue.getName(); + } + else + { + routingKey = body.getRoutingKey().intern(); + } + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + routingKey = body.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : body.getRoutingKey().intern(); + } + + if (queue == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + final Exchange exch = exchangeRegistry.getExchange(body.getExchange()); + if (exch == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.getExchange() + " does not exist."); + } + + + try + { + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.BIND, body, exch, queue, routingKey); + + if (!exch.isBound(routingKey, body.getArguments(), queue)) + { + queue.bind(exch, routingKey, body.getArguments()); + } + } + catch (AMQInvalidRoutingKeyException rke) + { + throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, routingKey.toString()); + } + catch (AMQException e) + { + throw body.getChannelException(AMQConstant.CHANNEL_ERROR, e.toString()); + } + + if (_log.isInfoEnabled()) + { + _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey); + } + if (!body.getNowait()) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createQueueBindOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java new file mode 100644 index 0000000000..3047643021 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -0,0 +1,209 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.UUID; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.configuration.Configured; + +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.commons.configuration.Configuration; + +public class QueueDeclareHandler implements StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(QueueDeclareHandler.class); + + private static final QueueDeclareHandler _instance = new QueueDeclareHandler(); + + public static QueueDeclareHandler getInstance() + { + return _instance; + } + + @Configured(path = "queue.auto_register", defaultValue = "true") + public boolean autoRegister; + + private final AtomicInteger _counter = new AtomicInteger(); + + + protected QueueDeclareHandler() + { + Configurator.configure(this); + } + + public void methodReceived(AMQStateManager stateManager, QueueDeclareBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore store = virtualHost.getMessageStore(); + + + if (!body.getPassive()) + { + //Perform ACL if request is not passive + virtualHost.getAccessManager().authorise(session, Permission.CREATE, body); + } + + + final AMQShortString queueName; + + // if we aren't given a queue name, we create one which we return to the client + + if ((body.getQueue() == null) || (body.getQueue().length() == 0)) + { + queueName = createName(); + } + else + { + queueName = body.getQueue().intern(); + } + + AMQQueue queue; + //TODO: do we need to check that the queue already exists with exactly the same "configuration"? + + synchronized (queueRegistry) + { + + + + if (((queue = queueRegistry.getQueue(queueName)) == null)) + { + + if (body.getPassive()) + { + String msg = "Queue: " + queueName + " not found on VirtualHost(" + virtualHost + ")."; + throw body.getChannelException(AMQConstant.NOT_FOUND, msg); + } + else + { + queue = createQueue(queueName, body, virtualHost, session); + if (queue.isDurable() && !queue.isAutoDelete()) + { + store.createQueue(queue, body.getArguments()); + } + queueRegistry.registerQueue(queue); + if (autoRegister) + { + Exchange defaultExchange = exchangeRegistry.getDefaultExchange(); + + queue.bind(defaultExchange, queueName, null); + _logger.info("Queue " + queueName + " bound to default exchange(" + defaultExchange.getName() + ")"); + } + } + } + else if (queue.getOwner() != null && !session.getContextKey().equals(queue.getOwner())) + { + throw body.getChannelException(AMQConstant.ALREADY_EXISTS, "Cannot declare queue('" + queueName + "')," + + " as exclusive queue with same name " + + "declared on another client ID('" + + queue.getOwner() + "')"); + } + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //set this as the default queue on the channel: + channel.setDefaultQueue(queue); + } + + if (!body.getNowait()) + { + MethodRegistry methodRegistry = session.getMethodRegistry(); + QueueDeclareOkBody responseBody = + methodRegistry.createQueueDeclareOkBody(queueName, + queue.getMessageCount(), + queue.getConsumerCount()); + session.writeFrame(responseBody.generateFrame(channelId)); + + _logger.info("Queue " + queueName + " declared successfully"); + } + } + + protected AMQShortString createName() + { + return new AMQShortString("tmp_" + UUID.randomUUID()); + } + + protected AMQQueue createQueue(final AMQShortString queueName, + QueueDeclareBody body, + VirtualHost virtualHost, + final AMQProtocolSession session) + throws AMQException + { + final QueueRegistry registry = virtualHost.getQueueRegistry(); + AMQShortString owner = body.getExclusive() ? session.getContextKey() : null; + + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost, + body.getArguments()); + + + if (body.getExclusive() && !body.getDurable()) + { + final AMQProtocolSession.Task deleteQueueTask = + new AMQProtocolSession.Task() + { + public void doTask(AMQProtocolSession session) throws AMQException + { + if (registry.getQueue(queueName) == queue) + { + queue.delete(); + } + } + }; + + session.addSessionCloseTask(deleteQueueTask); + + queue.addQueueDeleteTask(new AMQQueue.Task() + { + public void doTask(AMQQueue queue) + { + session.removeSessionCloseTask(deleteQueueTask); + } + }); + }// if exclusive and not durable + + return queue; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java new file mode 100644 index 0000000000..dfc36f5b93 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java @@ -0,0 +1,124 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.security.access.Permission; + +public class QueueDeleteHandler implements StateAwareMethodListener +{ + private static final QueueDeleteHandler _instance = new QueueDeleteHandler(); + + public static QueueDeleteHandler getInstance() + { + return _instance; + } + + private final boolean _failIfNotFound; + + public QueueDeleteHandler() + { + this(true); + } + + public QueueDeleteHandler(boolean failIfNotFound) + { + _failIfNotFound = failIfNotFound; + + } + + public void methodReceived(AMQStateManager stateManager, QueueDeleteBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore store = virtualHost.getMessageStore(); + + AMQQueue queue; + if (body.getQueue() == null) + { + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //get the default queue on the channel: + queue = channel.getDefaultQueue(); + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + } + + if (queue == null) + { + if (_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + } + else + { + if (body.getIfEmpty() && !queue.isEmpty()) + { + throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.getQueue() + " is not empty."); + } + else if (body.getIfUnused() && !queue.isUnused()) + { + // TODO - Error code + throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.getQueue() + " is still used."); + + } + else + { + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.DELETE, body, queue); + + int purged = queue.delete(); + + + if (queue.isDurable()) + { + store.removeQueue(queue); + } + + MethodRegistry methodRegistry = session.getMethodRegistry(); + QueueDeleteOkBody responseBody = methodRegistry.createQueueDeleteOkBody(purged); + session.writeFrame(responseBody.generateFrame(channelId)); + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java new file mode 100644 index 0000000000..7377862875 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueuePurgeBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.security.access.Permission; + +public class QueuePurgeHandler implements StateAwareMethodListener +{ + private static final QueuePurgeHandler _instance = new QueuePurgeHandler(); + + public static QueuePurgeHandler getInstance() + { + return _instance; + } + + private final boolean _failIfNotFound; + + public QueuePurgeHandler() + { + this(true); + } + + public QueuePurgeHandler(boolean failIfNotFound) + { + _failIfNotFound = failIfNotFound; + } + + public void methodReceived(AMQStateManager stateManager, QueuePurgeBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + AMQChannel channel = session.getChannel(channelId); + + + AMQQueue queue; + if(body.getQueue() == null) + { + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + //get the default queue on the channel: + queue = channel.getDefaultQueue(); + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED,"No queue specified."); + } + } + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + } + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + } + else + { + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.PURGE, body, queue); + + long purged = queue.clearQueue(channel.getStoreContext()); + + + if(!body.getNowait()) + { + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createQueuePurgeOkBody(purged); + session.writeFrame(responseBody.generateFrame(channelId)); + + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java new file mode 100644 index 0000000000..6331a0365d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java @@ -0,0 +1,134 @@ +package org.apache.qpid.server.handler; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.log4j.Logger; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.protocol.AMQConstant; + +public class QueueUnbindHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(QueueUnbindHandler.class); + + private static final QueueUnbindHandler _instance = new QueueUnbindHandler(); + + public static QueueUnbindHandler getInstance() + { + return _instance; + } + + private QueueUnbindHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, QueueUnbindBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + + final AMQQueue queue; + final AMQShortString routingKey; + + if (body.getQueue() == null) + { + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + queue = channel.getDefaultQueue(); + + if (queue == null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null"); + } + + routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern(); + + } + else + { + queue = queueRegistry.getQueue(body.getQueue()); + routingKey = body.getRoutingKey() == null ? null : body.getRoutingKey().intern(); + } + + if (queue == null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, "Queue " + body.getQueue() + " does not exist."); + } + final Exchange exch = exchangeRegistry.getExchange(body.getExchange()); + if (exch == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.getExchange() + " does not exist."); + } + + //Perform ACLs + virtualHost.getAccessManager().authorise(session, Permission.UNBIND, body, queue); + + try + { + queue.unBind(exch, routingKey, body.getArguments()); + } + catch (AMQInvalidRoutingKeyException rke) + { + throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, routingKey.toString()); + } + catch (AMQException e) + { + if(e.getErrorCode() == AMQConstant.NOT_FOUND) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND,e.getMessage(),e); + } + throw body.getChannelException(AMQConstant.CHANNEL_ERROR, e.toString()); + } + + if (_log.isInfoEnabled()) + { + _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + routingKey); + } + + MethodRegistry_0_9 methodRegistry = (MethodRegistry_0_9) session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createQueueUnbindOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java new file mode 100644 index 0000000000..9475b83c8f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl.java @@ -0,0 +1,566 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl implements MethodDispatcher +{ + private final AMQStateManager _stateManager; + + private static interface DispatcherFactory + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager); + } + + private static final Map _dispatcherFactories = + new HashMap(); + + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_8_0(stateManager); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public MethodDispatcher createMethodDispatcher(AMQStateManager stateManager) + { + return new ServerMethodDispatcherImpl_0_9(stateManager); + } + }); + + } + + + private static final AccessRequestHandler _accessRequestHandler = AccessRequestHandler.getInstance(); + private static final ChannelCloseHandler _channelCloseHandler = ChannelCloseHandler.getInstance(); + private static final ChannelOpenHandler _channelOpenHandler = ChannelOpenHandler.getInstance(); + private static final ChannelCloseOkHandler _channelCloseOkHandler = ChannelCloseOkHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionCloseOkMethodHandler _connectionCloseOkMethodHandler = ConnectionCloseOkMethodHandler.getInstance(); + private static final ConnectionOpenMethodHandler _connectionOpenMethodHandler = ConnectionOpenMethodHandler.getInstance(); + private static final ConnectionTuneOkMethodHandler _connectionTuneOkMethodHandler = ConnectionTuneOkMethodHandler.getInstance(); + private static final ConnectionSecureOkMethodHandler _connectionSecureOkMethodHandler = ConnectionSecureOkMethodHandler.getInstance(); + private static final ConnectionStartOkMethodHandler _connectionStartOkMethodHandler = ConnectionStartOkMethodHandler.getInstance(); + private static final ExchangeDeclareHandler _exchangeDeclareHandler = ExchangeDeclareHandler.getInstance(); + private static final ExchangeDeleteHandler _exchangeDeleteHandler = ExchangeDeleteHandler.getInstance(); + private static final ExchangeBoundHandler _exchangeBoundHandler = ExchangeBoundHandler.getInstance(); + private static final BasicAckMethodHandler _basicAckMethodHandler = BasicAckMethodHandler.getInstance(); + private static final BasicRecoverMethodHandler _basicRecoverMethodHandler = BasicRecoverMethodHandler.getInstance(); + private static final BasicConsumeMethodHandler _basicConsumeMethodHandler = BasicConsumeMethodHandler.getInstance(); + private static final BasicGetMethodHandler _basicGetMethodHandler = BasicGetMethodHandler.getInstance(); + private static final BasicCancelMethodHandler _basicCancelMethodHandler = BasicCancelMethodHandler.getInstance(); + private static final BasicPublishMethodHandler _basicPublishMethodHandler = BasicPublishMethodHandler.getInstance(); + private static final BasicQosHandler _basicQosHandler = BasicQosHandler.getInstance(); + private static final QueueBindHandler _queueBindHandler = QueueBindHandler.getInstance(); + private static final QueueDeclareHandler _queueDeclareHandler = QueueDeclareHandler.getInstance(); + private static final QueueDeleteHandler _queueDeleteHandler = QueueDeleteHandler.getInstance(); + private static final QueuePurgeHandler _queuePurgeHandler = QueuePurgeHandler.getInstance(); + private static final ChannelFlowHandler _channelFlowHandler = ChannelFlowHandler.getInstance(); + private static final TxSelectHandler _txSelectHandler = TxSelectHandler.getInstance(); + private static final TxCommitHandler _txCommitHandler = TxCommitHandler.getInstance(); + private static final TxRollbackHandler _txRollbackHandler = TxRollbackHandler.getInstance(); + private static final BasicRejectMethodHandler _basicRejectMethodHandler = BasicRejectMethodHandler.getInstance(); + + + + public static MethodDispatcher createMethodDispatcher(AMQStateManager stateManager, ProtocolVersion protocolVersion) + { + return _dispatcherFactories.get(protocolVersion).createMethodDispatcher(stateManager); + } + + + public ServerMethodDispatcherImpl(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + + protected AMQStateManager getStateManager() + { + return _stateManager; + } + + + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + _accessRequestHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + _basicAckMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + _basicCancelMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + _basicConsumeMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + _basicGetMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + _basicPublishMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + _basicQosHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + _basicRecoverMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + _basicRejectMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + _channelOpenHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + _channelCloseOkHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + _channelFlowHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + _connectionOpenMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + _connectionCloseOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + _connectionSecureOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + _connectionStartOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + _connectionTuneOkMethodHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + _exchangeBoundHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + _exchangeDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + _exchangeDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + _queueBindHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + _queueDeclareHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + _queueDeleteHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + _queuePurgeHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + _txCommitHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + _txRollbackHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + _txSelectHandler.methodReceived(_stateManager, body, channelId); + return true; + } + + + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java new file mode 100644 index 0000000000..8b1dca77ba --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_0_9.java @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + + +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + + + +public class ServerMethodDispatcherImpl_0_9 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_0_9 + +{ + + private static final BasicRecoverSyncMethodHandler _basicRecoverSyncMethodHandler = + BasicRecoverSyncMethodHandler.getInstance(); + private static final QueueUnbindHandler _queueUnbindHandler = + QueueUnbindHandler.getInstance(); + + + public ServerMethodDispatcherImpl_0_9(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + _basicRecoverSyncMethodHandler.methodReceived(getStateManager(), body, channelId); + return true; + } + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + _queueUnbindHandler.methodReceived(getStateManager(),body,channelId); + return true; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.java new file mode 100644 index 0000000000..d599ca3d4e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ServerMethodDispatcherImpl_8_0.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.handler; + +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.AMQException; + +public class ServerMethodDispatcherImpl_8_0 + extends ServerMethodDispatcherImpl + implements MethodDispatcher_8_0 +{ + public ServerMethodDispatcherImpl_8_0(AMQStateManager stateManager) + { + super(stateManager); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + throw new UnexpectedMethodException(body); + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java new file mode 100644 index 0000000000..9b23d88838 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java @@ -0,0 +1,78 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.TxCommitBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class TxCommitHandler implements StateAwareMethodListener +{ + private static final Logger _log = Logger.getLogger(TxCommitHandler.class); + + private static TxCommitHandler _instance = new TxCommitHandler(); + + public static TxCommitHandler getInstance() + { + return _instance; + } + + private TxCommitHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, TxCommitBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + try + { + if (_log.isDebugEnabled()) + { + _log.debug("Commit received on channel " + channelId); + } + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.commit(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createTxCommitOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + channel.processReturns(); + } + catch (AMQException e) + { + throw body.getChannelException(e.getErrorCode(), "Failed to commit: " + e.getMessage()); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java new file mode 100644 index 0000000000..5f402f3fda --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.TxRollbackBody; +import org.apache.qpid.framing.TxRollbackOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class TxRollbackHandler implements StateAwareMethodListener +{ + private static TxRollbackHandler _instance = new TxRollbackHandler(); + + public static TxRollbackHandler getInstance() + { + return _instance; + } + + private TxRollbackHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, TxRollbackBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + try + { + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.rollback(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + AMQMethodBody responseBody = methodRegistry.createTxRollbackOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + + + //Now resend all the unacknowledged messages back to the original subscribers. + //(Must be done after the TxnRollback-ok response). + // Why, are we not allowed to send messages back to client before the ok method? + channel.resend(false); + } + catch (AMQException e) + { + throw body.getChannelException(e.getErrorCode(), "Failed to rollback: " + e.getMessage()); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java new file mode 100644 index 0000000000..308f5b73cf --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.TxSelectBody; +import org.apache.qpid.framing.TxSelectOkBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.AMQChannel; + +public class TxSelectHandler implements StateAwareMethodListener +{ + private static TxSelectHandler _instance = new TxSelectHandler(); + + public static TxSelectHandler getInstance() + { + return _instance; + } + + private TxSelectHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, TxSelectBody body, int channelId) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw body.getChannelNotFoundException(channelId); + } + + channel.setLocalTransactional(); + + MethodRegistry methodRegistry = session.getMethodRegistry(); + TxSelectOkBody responseBody = methodRegistry.createTxSelectOkBody(); + session.writeFrame(responseBody.generateFrame(channelId)); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.java new file mode 100644 index 0000000000..fb18519fe1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/UnexpectedMethodException.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.handler; + + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class UnexpectedMethodException extends AMQException +{ + public UnexpectedMethodException(AMQMethodBody body) + { + super("Unexpected method recevied: " + body.getClass().getName()); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java new file mode 100644 index 0000000000..c08fae4e4e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java @@ -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. + * + */ +package org.apache.qpid.server.jms; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.protocol.AMQProtocolSession; + +public class JmsConsumer +{ + private int _prefetchValue; + + private PrefetchUnits _prefetchUnits; + + private boolean _noLocal; + + private boolean _autoAck; + + private boolean _exclusive; + + private AMQProtocolSession _protocolSession; + + public enum PrefetchUnits + { + OCTETS, + MESSAGES + } + + public int getPrefetchValue() + { + return _prefetchValue; + } + + public void setPrefetchValue(int prefetchValue) + { + _prefetchValue = prefetchValue; + } + + public PrefetchUnits getPrefetchUnits() + { + return _prefetchUnits; + } + + public void setPrefetchUnits(PrefetchUnits prefetchUnits) + { + _prefetchUnits = prefetchUnits; + } + + public boolean isNoLocal() + { + return _noLocal; + } + + public void setNoLocal(boolean noLocal) + { + _noLocal = noLocal; + } + + public boolean isAutoAck() + { + return _autoAck; + } + + public void setAutoAck(boolean autoAck) + { + _autoAck = autoAck; + } + + public boolean isExclusive() + { + return _exclusive; + } + + public void setExclusive(boolean exclusive) + { + _exclusive = exclusive; + } + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void setProtocolSession(AMQProtocolSession protocolSession) + { + _protocolSession = protocolSession; + } + + public void deliverMessage() throws AMQException + { + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java new file mode 100644 index 0000000000..a2c2bd62a2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.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.management; + +import javax.management.ListenerNotFoundException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +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 +{ + /** + * broadcaster support class + */ + protected NotificationBroadcasterSupport _broadcaster = new NotificationBroadcasterSupport(); + + /** + * sequence number for notifications + */ + protected long _notificationSequenceNumber = 0; + + protected MBeanInfo _mbeanInfo; + + protected AMQManagedObject(Class managementInterface, String typeName) + throws NotCompliantMBeanException + { + super(managementInterface, typeName); + buildMBeanInfo(); + } + + @Override + public MBeanInfo getMBeanInfo() + { + return _mbeanInfo; + } + + private void buildMBeanInfo() throws NotCompliantMBeanException + { + _mbeanInfo = new MBeanInfo(this.getClass().getName(), + MBeanIntrospector.getMBeanDescription(this.getClass()), + MBeanIntrospector.getMBeanAttributesInfo(getManagementInterface()), + MBeanIntrospector.getMBeanConstructorsInfo(this.getClass()), + MBeanIntrospector.getMBeanOperationsInfo(getManagementInterface()), + this.getNotificationInfo()); + } + + + + // notification broadcaster implementation + + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + { + _broadcaster.addNotificationListener(listener, filter, handback); + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException + { + _broadcaster.removeNotificationListener(listener); + } + + public MBeanNotificationInfo[] getNotificationInfo() + { + return null; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java new file mode 100644 index 0000000000..84526dbc11 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java @@ -0,0 +1,191 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.management; + +import javax.management.JMException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.registry.ApplicationRegistry; + +/** + * 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 Class _managementInterface; + + private String _typeName; + + protected DefaultManagedObject(Class managementInterface, String typeName) + throws NotCompliantMBeanException + { + super(managementInterface); + _managementInterface = managementInterface; + _typeName = typeName; + } + + public String getType() + { + return _typeName; + } + + public Class getManagementInterface() + { + return _managementInterface; + } + + public ManagedObject getParentObject() + { + return null; + } + + public void register() throws AMQException + { + try + { + getManagedObjectRegistry().registerObject(this); + } + catch (JMException e) + { + throw new AMQException("Error registering managed object " + this + ": " + e, e); + } + } + + protected ManagedObjectRegistry getManagedObjectRegistry() + { + return ApplicationRegistry.getInstance().getManagedObjectRegistry(); + } + + public void unregister() throws AMQException + { + try + { + getManagedObjectRegistry().unregisterObject(this); + } + catch (JMException e) + { + throw new AMQException("Error unregistering managed object: " + this + ": " + e, e); + } + } + + 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 ""; + } + + protected static StringBuffer jmxEncode(StringBuffer jmxName, int attrPos) + { + for (int i = attrPos; i < jmxName.length(); i++) + { + if (jmxName.charAt(i) == ',') + { + jmxName.setCharAt(i, ';'); + } + else if (jmxName.charAt(i) == ':') + { + jmxName.setCharAt(i, '-'); + } + else if (jmxName.charAt(i) == '?' || + jmxName.charAt(i) == '*' || + jmxName.charAt(i) == '\\') + { + jmxName.insert(i, '\\'); + i++; + } + else if (jmxName.charAt(i) == '\n') + { + jmxName.insert(i, '\\'); + i++; + jmxName.setCharAt(i, 'n'); + } + } + return jmxName; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java new file mode 100644 index 0000000000..659f806d58 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -0,0 +1,224 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser; +import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser; + +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.MBeanServerForwarder; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import java.util.HashMap; +import java.util.Map; + +/** + * This class starts up an MBeanserver. If out of the box agent is being used then there are no security features + * implemented. To use the security features like user authentication, turn off the jmx options in the "QPID_OPTS" env + * variable and use JMXMP connector server. If JMXMP connector is not available, then the standard JMXConnector will be + * used, which again doesn't have user authentication. + */ +public class JMXManagedObjectRegistry implements ManagedObjectRegistry +{ + private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class); + + private final MBeanServer _mbeanServer; + private Registry _rmiRegistry; + private JMXServiceURL _jmxURL; + + public static final String MANAGEMENT_PORT_CONFIG_PATH = "management.jmxport"; + public static final int MANAGEMENT_PORT_DEFAULT = 8999; + + public JMXManagedObjectRegistry() throws AMQException + { + _log.info("Initialising managed object registry using platform MBean server"); + IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); + + // Retrieve the config parameters + boolean platformServer = appRegistry.getConfiguration().getBoolean("management.platform-mbeanserver", true); + + _mbeanServer = + platformServer ? ManagementFactory.getPlatformMBeanServer() + : MBeanServerFactory.createMBeanServer(ManagedObject.DOMAIN); + } + + + public void start() throws IOException + { + // Check if the "QPID_OPTS" is set to use Out of the Box JMXAgent + if (areOutOfTheBoxJMXOptionsSet()) + { + _log.info("JMX: Using the out of the box JMX Agent"); + return; + } + + IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); + + boolean security = appRegistry.getConfiguration().getBoolean("management.security-enabled", false); + int port = appRegistry.getConfiguration().getInt(MANAGEMENT_PORT_CONFIG_PATH, MANAGEMENT_PORT_DEFAULT); + + if (security) + { + // For SASL using JMXMP + _jmxURL = new JMXServiceURL("jmxmp", null, port); + + Map env = new HashMap(); + Map map = appRegistry.getDatabaseManager().getDatabases(); + PrincipalDatabase db = null; + + for (Map.Entry entry : map.entrySet()) + { + if (entry.getValue() instanceof Base64MD5PasswordFilePrincipalDatabase) + { + db = entry.getValue(); + break; + } + else if (entry.getValue() instanceof PlainPasswordFilePrincipalDatabase) + { + db = entry.getValue(); + } + } + + if (db instanceof Base64MD5PasswordFilePrincipalDatabase) + { + env.put("jmx.remote.profiles", "SASL/CRAM-MD5"); + CRAMMD5HashedInitialiser initialiser = new CRAMMD5HashedInitialiser(); + initialiser.initialise(db); + env.put("jmx.remote.sasl.callback.handler", initialiser.getCallbackHandler()); + } + else if (db instanceof PlainPasswordFilePrincipalDatabase) + { + PlainInitialiser initialiser = new PlainInitialiser(); + initialiser.initialise(db); + env.put("jmx.remote.sasl.callback.handler", initialiser.getCallbackHandler()); + env.put("jmx.remote.profiles", "SASL/PLAIN"); + } + + //workaround NPE generated from env map classloader issue when using Eclipse 3.4 to launch + env.put("jmx.remote.profile.provider.class.loader", this.getClass().getClassLoader()); + + // Enable the SSL security and server authentication + /* + SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory(); + SslRMIServerSocketFactory ssf = new SslRMIServerSocketFactory(); + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); + env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf); + */ + + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, env, _mbeanServer); + MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance(); + cs.setMBeanServerForwarder(mbsf); + cs.start(); + _log.warn("JMX: Started JMXConnector server on port '" + port + "' with SASL"); + + } + else + { + startJMXConnectorServer(port); + _log.warn("JMX: Started JMXConnector server on port '" + port + "' with security disabled"); + } + } + + /** + * Starts up an RMIRegistry at configured port and attaches a JMXConnectorServer to it. + * + * @param port + * + * @throws IOException + */ + private void startJMXConnectorServer(int port) throws IOException + { + startRMIRegistry(port); + _jmxURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi"); + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(_jmxURL, null, _mbeanServer); + cs.start(); + } + + public void registerObject(ManagedObject managedObject) throws JMException + { + _mbeanServer.registerMBean(managedObject, managedObject.getObjectName()); + } + + public void unregisterObject(ManagedObject managedObject) throws JMException + { + _mbeanServer.unregisterMBean(managedObject.getObjectName()); + } + + /** + * Checks is the "QPID_OPTS" env variable is set to use the out of the box JMXAgent. + * + * @return + */ + 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; + } + + /** + * Starts the rmi registry at given port + * + * @param port + * + * @throws RemoteException + */ + private void startRMIRegistry(int port) throws RemoteException + { + System.setProperty("java.rmi.server.randomIDs", "true"); + _rmiRegistry = LocateRegistry.createRegistry(port); + } + + // stops the RMIRegistry, if it was running and bound to a port + public void close() throws RemoteException + { + if (_rmiRegistry != null) + { + // Stopping the RMI registry + UnicastRemoteObject.unexportObject(_rmiRegistry, true); + } + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java new file mode 100644 index 0000000000..7d42297699 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for MBean attributes. This should be used with getter or setter + * methods of attributes. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Inherited +public @interface MBeanAttribute +{ + String name(); + String description(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java new file mode 100644 index 0000000000..9138e03085 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.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.server.management; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for MBean constructors. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.CONSTRUCTOR) +@Inherited +public @interface MBeanConstructor +{ + String value(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java new file mode 100644 index 0000000000..448fed3280 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.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.management; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for MBean class. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface MBeanDescription { + String value(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java new file mode 100644 index 0000000000..0c2ec2aebd --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java @@ -0,0 +1,388 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.NotCompliantMBeanException; + +/** + * 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"; + + /** + * 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); + //MBeanConstructorInfo constructorInfo = new MBeanConstructorInfo("desc", 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 = null; + Annotation anno = cons.getAnnotation(MBeanConstructor.class); + if (anno != null && MBeanConstructor.class.isInstance(anno)) + { + desc = MBeanConstructor.class.cast(anno).value(); + } + + //MBeanParameterInfo[] paramsInfo = getParametersInfo(cons.getParameterAnnotations(), + // cons.getParameterTypes()); + + return new MBeanConstructorInfo(cons.getName(), + desc != null ? _defaultConstructorDescription : 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/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java new file mode 100644 index 0000000000..a0ecc2bd85 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.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.management; + +import org.apache.qpid.server.security.access.management.UserManagement; +import org.apache.log4j.Logger; + +import javax.management.remote.MBeanServerForwarder; +import javax.management.remote.JMXPrincipal; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.JMException; +import javax.security.auth.Subject; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.Principal; +import java.security.AccessControlContext; +import java.util.Set; +import java.util.Properties; + +/** + * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. This implements + * the logic for allowing the users to invoke MBean operations and implements the restrictions for readOnly, readWrite + * and admin users. + */ +public class MBeanInvocationHandlerImpl implements InvocationHandler +{ + private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class); + + public final static String ADMIN = "admin"; + public final static String READWRITE = "readwrite"; + public final static String READONLY = "readonly"; + private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate"; + private MBeanServer mbs; + private static Properties _userRoles = new Properties(); + + 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); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + final 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; + } + + // Retrieve Subject from current AccessControlContext + AccessControlContext acc = AccessController.getContext(); + Subject subject = Subject.getSubject(acc); + + // Allow operations performed locally on behalf of the connector server itself + if (subject == null) + { + return method.invoke(mbs, args); + } + + if (args == null || DELEGATE.equals(args[0])) + { + return method.invoke(mbs, args); + } + + // Restrict access to "createMBean" and "unregisterMBean" to any user + if (methodName.equals("createMBean") || methodName.equals("unregisterMBean")) + { + _logger.debug("User trying to create or unregister an MBean"); + throw new SecurityException("Access denied"); + } + + // Retrieve JMXPrincipal from Subject + Set principals = subject.getPrincipals(JMXPrincipal.class); + if (principals == null || principals.isEmpty()) + { + throw new SecurityException("Access denied"); + } + + Principal principal = principals.iterator().next(); + String identity = principal.getName(); + + if (isAdminMethod(args)) + { + if (isAdmin(identity)) + { + return method.invoke(mbs, args); + } + else + { + throw new SecurityException("Access denied"); + } + } + + // Following users can perform any operation other than "createMBean" and "unregisterMBean" + if (isAllowedToModify(identity)) + { + return method.invoke(mbs, args); + } + + // These users can only call "getAttribute" on the MBeanServerDelegate MBean + // Here we can add other fine grained permissions like specific method for a particular mbean + if (isReadOnlyUser(identity) && isReadOnlyMethod(method, args)) + { + return method.invoke(mbs, args); + } + + throw new SecurityException("Access denied"); + } + + private boolean isAdminMethod(Object[] args) + { + if (args[0] instanceof ObjectName) + { + ObjectName object = (ObjectName) args[0]; + return UserManagement.TYPE.equals(object.getKeyProperty("type")); + } + + return false; + } + + // Initialises the user roles + public static void setAccessRights(Properties accessRights) + { + _userRoles = accessRights; + } + + private boolean isAdmin(String userName) + { + if (ADMIN.equals(_userRoles.getProperty(userName))) + { + return true; + } + return false; + } + + private boolean isAllowedToModify(String userName) + { + if (ADMIN.equals(_userRoles.getProperty(userName)) + || READWRITE.equals(_userRoles.getProperty(userName))) + { + return true; + } + return false; + } + + private boolean isReadOnlyUser(String userName) + { + if (READONLY.equals(_userRoles.getProperty(userName))) + { + return true; + } + return false; + } + + private boolean isReadOnlyMethod(Method method, Object[] args) + { + String methodName = method.getName(); + if (methodName.startsWith("query") || methodName.startsWith("get")) + { + return true; + } + else if (methodName.startsWith("set")) + { + return false; + } + + if ((args[0] instanceof ObjectName) && (methodName.equals("invoke"))) + { + String mbeanMethod = (args.length > 1) ? (String) args[1] : null; + if (mbeanMethod == null) + { + return false; + } + + try + { + MBeanInfo mbeanInfo = mbs.getMBeanInfo((ObjectName) args[0]); + if (mbeanInfo != null) + { + MBeanOperationInfo[] opInfos = mbeanInfo.getOperations(); + for (MBeanOperationInfo opInfo : opInfos) + { + if (opInfo.getName().equals(mbeanMethod) && (opInfo.getImpact() == MBeanOperationInfo.INFO)) + { + return true; + } + } + } + } + catch (JMException ex) + { + ex.printStackTrace(); + } + } + + return false; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java new file mode 100644 index 0000000000..a2dca3e51d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.management.MBeanOperationInfo; + +/** + * Annotation for MBean operations. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Inherited +public @interface MBeanOperation +{ + String name(); + String description(); + int impact() default MBeanOperationInfo.INFO; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java new file mode 100644 index 0000000000..aba5ec70d8 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.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.management; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for MBean operation parameters. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface MBeanOperationParameter { + String name(); + String description(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java new file mode 100644 index 0000000000..166a2a376d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java new file mode 100644 index 0000000000..45e2e91ed7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java @@ -0,0 +1,98 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.management; + +import java.io.IOException; + +import javax.management.JMException; +import javax.management.MBeanOperationInfo; + +import org.apache.qpid.server.exchange.ManagedExchange; +import org.apache.qpid.server.queue.ManagedQueue; + +/** + * The ManagedBroker is the management interface to expose management + * features of the Broker. + * + * @author Bhupendra Bhardwaj + * @version 0.1 + */ +public interface ManagedBroker +{ + static final String TYPE = "VirtualHostManager"; + + /** + * Creates a new Exchange. + * @param name + * @param type + * @param durable + * @param passive + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="createNewExchange", description="Creates a new Exchange", impact= MBeanOperationInfo.ACTION) + void createNewExchange(@MBeanOperationParameter(name="name", description="Name of the new exchange")String name, + @MBeanOperationParameter(name="ExchangeType", description="Type of the exchange")String type, + @MBeanOperationParameter(name="durable", description="true if the Exchang should be durable")boolean durable) + throws IOException, JMException; + + /** + * unregisters all the channels, queuebindings etc and unregisters + * this exchange from managed objects. + * @param exchange + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="unregisterExchange", + description="Unregisters all the related channels and queuebindings of this exchange", + impact= MBeanOperationInfo.ACTION) + void unregisterExchange(@MBeanOperationParameter(name= ManagedExchange.TYPE, description="Exchange Name")String exchange) + throws IOException, JMException; + + /** + * Create a new Queue on the Broker server + * @param queueName + * @param durable + * @param owner + * @param autoDelete + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="createNewQueue", description="Create a new Queue on the Broker server", impact= MBeanOperationInfo.ACTION) + void createNewQueue(@MBeanOperationParameter(name="queue name", description="Name of the new queue")String queueName, + @MBeanOperationParameter(name="owner", description="Owner name")String owner, + @MBeanOperationParameter(name="durable", description="true if the queue should be durable")boolean durable) + throws IOException, JMException; + + /** + * Unregisters the Queue bindings, removes the subscriptions and unregisters + * from the managed objects. + * @param queueName + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="deleteQueue", + description="Unregisters the Queue bindings, removes the subscriptions and deletes the queue", + impact= MBeanOperationInfo.ACTION) + void deleteQueue(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue Name")String queueName) + throws IOException, JMException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java new file mode 100644 index 0000000000..42ea8921a4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.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.management; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.apache.qpid.AMQException; + +/** + * 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; + + void unregister() throws AMQException; + + /** + * Returns the ObjectName required for the mbeanserver registration. + * @return ObjectName + * @throws MalformedObjectNameException + */ + ObjectName getObjectName() throws MalformedObjectNameException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java new file mode 100644 index 0000000000..d8d87ef881 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/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.management; + +import javax.management.JMException; +import java.rmi.RemoteException; +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 +{ + void start() throws IOException; + + void registerObject(ManagedObject managedObject) throws JMException; + + void unregisterObject(ManagedObject managedObject) throws JMException; + + void close() throws RemoteException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java new file mode 100644 index 0000000000..042f626e8b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.management; + +import org.apache.qpid.configuration.Configured; + +public class ManagementConfiguration +{ + @Configured(path = "management.enabled", + defaultValue = "true") + public boolean enabled; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java new file mode 100644 index 0000000000..b4fbed6948 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.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.management; + +import javax.management.JMException; + +import org.apache.log4j.Logger; + +import java.rmi.RemoteException; + +/** + * 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() throws RemoteException + { + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java new file mode 100644 index 0000000000..e01c5aabbf --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.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. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.AMQException; + +public interface ProtocolOutputConverter +{ + void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag); + + interface Factory + { + ProtocolOutputConverter newInstance(AMQProtocolSession session); + } + + void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException; + + void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException; + + byte getProtocolMinorVersion(); + + byte getProtocolMajorVersion(); + + void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException; + + void writeFrame(AMQDataBlock block); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java new file mode 100644 index 0000000000..36e7e88fd6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output; + +import org.apache.qpid.server.output.ProtocolOutputConverter.Factory; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.framing.ProtocolVersion; + +import java.util.Map; +import java.util.HashMap; + +public class ProtocolOutputConverterRegistry +{ + + private static final Map _registry = + new HashMap(); + + + static + { + register(ProtocolVersion.v8_0, org.apache.qpid.server.output.amqp0_8.ProtocolOutputConverterImpl.getInstanceFactory()); + register(ProtocolVersion.v0_9, org.apache.qpid.server.output.amqp0_9.ProtocolOutputConverterImpl.getInstanceFactory()); + + } + + private static void register(ProtocolVersion version, Factory converter) + { + + _registry.put(version,converter); + } + + + public static ProtocolOutputConverter getConverter(AMQProtocolSession session) + { + return _registry.get(session.getProtocolVersion()).newInstance(session); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java new file mode 100644 index 0000000000..2b55d294b5 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -0,0 +1,284 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/* + * This file is auto-generated by Qpid Gentools v.0.1 - do not modify. + * Supported AMQP versions: + * 8-0 + */ +package org.apache.qpid.server.output.amqp0_8; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQDataBlock deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + + + final int bodyCount = messageHandle.getBodyCount(storeContext); + + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + + AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + private AMQDataBlock createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicDeliverBody deliverBody = + methodRegistry.createBasicDeliverBody(consumerTag, + deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey()); + AMQFrame deliverFrame = deliverBody.generateFrame(channelId); + + + return deliverFrame; + } + + private AMQDataBlock createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicGetOkBody getOkBody = + methodRegistry.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicReturnBody basicReturnBody = + methodRegistry.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0); + BasicCancelOkBody basicCancelOkBody = methodRegistry.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java new file mode 100644 index 0000000000..65184fe744 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java @@ -0,0 +1,391 @@ +package org.apache.qpid.server.output.amqp0_9; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.mina.common.ByteBuffer; + +import java.util.Iterator; + +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQMessageHandle; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public class ProtocolOutputConverterImpl implements ProtocolOutputConverter +{ + private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9); + private static final ProtocolVersionMethodConverter PROTOCOL_METHOD_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter(); + + + public static Factory getInstanceFactory() + { + return new Factory() + { + + public ProtocolOutputConverter newInstance(AMQProtocolSession session) + { + return new ProtocolOutputConverterImpl(session); + } + }; + } + + private final AMQProtocolSession _protocolSession; + + private ProtocolOutputConverterImpl(AMQProtocolSession session) + { + _protocolSession = session; + } + + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + AMQBody deliverBody = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + final ContentHeaderBody contentHeaderBody = message.getContentHeaderBody(); + + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + + + final int bodyCount = messageHandle.getBodyCount(storeContext); + + if(bodyCount == 0) + { + SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody, + contentHeaderBody); + + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext, 0); + + AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb); + + CompositeAMQBodyBlock compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + } + + private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody) + { + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + contentHeaderBody); + return contentHeader; + } + + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + + AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext); + if(bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + writeFrame(compositeBlock); + } + else + { + + + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + ContentChunk cb = messageHandle.getContentChunk(storeContext, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)); + AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, i); + writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb))); + } + + + } + + + } + + + private AMQBody createEncodedDeliverFrame(AMQMessage message, final int channelId, final long deliveryTag, final AMQShortString consumerTag) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + final boolean isRedelivered = messageHandle.isRedelivered(); + final AMQShortString exchangeName = pb.getExchange(); + final AMQShortString routingKey = pb.getRoutingKey(); + + final AMQBody returnBlock = new AMQBody() + { + + public AMQBody _underlyingBody; + + public AMQBody createAMQBody() + { + return METHOD_REGISTRY.createBasicDeliverBody(consumerTag, + deliveryTag, + isRedelivered, + exchangeName, + routingKey); + + + + + + } + + public byte getFrameType() + { + return AMQMethodBody.TYPE; + } + + public int getSize() + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + return _underlyingBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + if(_underlyingBody == null) + { + _underlyingBody = createAMQBody(); + } + _underlyingBody.writePayload(buffer); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) + throws AMQException + { + throw new AMQException("This block should never be dispatched!"); + } + }; + return returnBlock; + } + + private AMQFrame createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + + BasicGetOkBody getOkBody = + METHOD_REGISTRY.createBasicGetOkBody(deliveryTag, + messageHandle.isRedelivered(), + pb.getExchange(), + pb.getRoutingKey(), + queueSize); + AMQFrame getOkFrame = getOkBody.generateFrame(channelId); + + return getOkFrame; + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private AMQDataBlock createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + + BasicReturnBody basicReturnBody = + METHOD_REGISTRY.createBasicReturnBody(replyCode, + replyText, + message.getMessagePublishInfo().getExchange(), + message.getMessagePublishInfo().getRoutingKey()); + AMQFrame returnFrame = basicReturnBody.generateFrame(channelId); + + return returnFrame; + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + AMQDataBlock returnFrame = createEncodedReturnFrame(message, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody()); + + Iterator bodyFrameIterator = message.getBodyFrameIterator(getProtocolSession(), channelId); + // + // Optimise the case where we have a single content body. In that case we create a composite block + // so that we can writeDeliver out the deliver, header and body with a single network writeDeliver. + // + if (bodyFrameIterator.hasNext()) + { + AMQDataBlock firstContentBody = bodyFrameIterator.next(); + AMQDataBlock[] blocks = new AMQDataBlock[]{returnFrame, contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(new AMQDataBlock[]{returnFrame, contentHeader}); + + writeFrame(compositeBlock); + } + + // + // Now start writing out the other content bodies + // TODO: MINA needs to be fixed so the the pending writes buffer is not unbounded + // + while (bodyFrameIterator.hasNext()) + { + writeFrame(bodyFrameIterator.next()); + } + } + + + public void writeFrame(AMQDataBlock block) + { + getProtocolSession().writeFrame(block); + } + + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + + BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag); + writeFrame(basicCancelOkBody.generateFrame(channelId)); + + } + + + public static final class CompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final AMQBody _contentBody; + private final int _channel; + + + public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + _contentBody = contentBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody); + } + } + + public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock + { + public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead(); + + private final AMQBody _methodBody; + private final AMQBody _headerBody; + private final int _channel; + + + public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody) + { + _channel = channel; + _methodBody = methodBody; + _headerBody = headerBody; + + } + + public long getSize() + { + return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ; + } + + public void writePayload(ByteBuffer buffer) + { + AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody); + } + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/Activator.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/Activator.java new file mode 100644 index 0000000000..b0ebf197f9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/Activator.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.qpid.server.plugins; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator +{ + + BundleContext _context = null; + + public void start(BundleContext ctx) throws Exception + { + _context = ctx; + } + + public void stop(BundleContext ctx) throws Exception + { + start(null); + } + + public BundleContext getContext() + { + return _context; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java new file mode 100644 index 0000000000..9191ecf6ed --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.qpid.server.plugins; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.felix.framework.Felix; +import org.apache.felix.framework.cache.BundleCache; +import org.apache.felix.framework.util.FelixConstants; +import org.apache.felix.framework.util.StringMap; +import org.apache.qpid.server.exchange.ExchangeType; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleException; +import org.osgi.util.tracker.ServiceTracker; + +/** + * + * @author aidan + * + * Provides access to pluggable elements, such as exchanges + */ + +public class PluginManager +{ + + private Felix _felix = null; + private ServiceTracker _exchangeTracker = null; + private Activator _activator = null; + private boolean _empty; + + public PluginManager(String plugindir) throws Exception + { + StringMap configMap = new StringMap(false); + + // Tell felix it's being embedded + configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true"); + // Add the bundle provided service interface package and the core OSGi + // packages to be exported from the class path via the system bundle. + configMap.put(FelixConstants.FRAMEWORK_SYSTEMPACKAGES, "org.osgi.framework; version=1.3.0," + + "org.osgi.service.packageadmin; version=1.2.0," + + "org.osgi.service.startlevel; version=1.0.0," + + "org.osgi.service.url; version=1.0.0," + + "org.apache.qpid.framing; version=0.2.1," + + "org.apache.qpid.server.exchange; version=0.2.1," + + "org.apache.qpid.server.management; version=0.2.1,"+ + "org.apache.qpid.protocol; version=0.2.1,"+ + "org.apache.qpid.server.virtualhost; version=0.2.1," + + "org.apache.qpid; version=0.2.1," + + "org.apache.qpid.server.queue; version=0.2.1," + + "javax.management.openmbean; version=1.0.0,"+ + "javax.management; version=1.0.0,"+ + "org.apache.qpid.junit.extensions.util; version=0.6.1," + ); + + if (plugindir == null) + { + _empty = true; + return; + } + + // Set the list of bundles to load + File dir = new File(plugindir); + if (!dir.exists()) + { + _empty = true; + return; + } + StringBuffer pluginJars = new StringBuffer(); + + if (dir.isDirectory()) + { + for (String child : dir.list()) + { + if (child.endsWith("jar")) + { + pluginJars.append(String.format(" file:%s%s%s", plugindir,File.separator,child)); + } + } + } + if (pluginJars.length() == 0) + { + _empty = true; + return; + } + + configMap.put(FelixConstants.AUTO_START_PROP + ".1", pluginJars.toString()); + configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, plugindir); + + List activators = new ArrayList(); + _activator = new Activator(); + activators.add(_activator); + + _felix = new Felix(configMap, activators); + try + { + _felix.start(); + _exchangeTracker = new ServiceTracker(_activator.getContext(), ExchangeType.class.getName(), null); + _exchangeTracker.open(); + } + catch (BundleException e) + { + throw new Exception("Could not create bundle"); + } + } + + public Map> getExchanges() + { + if (_empty) + { + return null; + } + Map>exchanges = new HashMap>(); + for (Object service : _exchangeTracker.getServices()) + { + if (service instanceof ExchangeType) + { + exchanges.put(service.getClass().getName(), (ExchangeType) service); + } + } + + return exchanges; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java new file mode 100644 index 0000000000..4b69842c49 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java @@ -0,0 +1,859 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.mina.common.IdleStatus; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.CloseFuture; +import org.apache.mina.transport.vmpipe.VmPipeAddress; + +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.codec.AMQDecoder; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.framing.*; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.handler.ServerMethodDispatcherImpl; +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.registry.ApplicationRegistry; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.transport.Sender; + +import javax.management.JMException; +import javax.security.sasl.SaslServer; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.Principal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +public class AMQMinaProtocolSession implements AMQProtocolSession, Managable +{ + private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class); + + private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); + + // to save boxing the channelId and looking up in a map... cache in an array the low numbered + // channels. This value must be of the form 2^x - 1. + private static final int CHANNEL_CACHE_SIZE = 0xff; + + private final IoSession _minaProtocolSession; + + private AMQShortString _contextKey; + + private AMQShortString _clientVersion = null; + + private VirtualHost _virtualHost; + + private final Map _channelMap = new HashMap(); + + private final AMQChannel[] _cachedChannels = new AMQChannel[CHANNEL_CACHE_SIZE + 1]; + + private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); + + private final AMQStateManager _stateManager; + + private AMQCodecFactory _codecFactory; + + private AMQProtocolSessionMBean _managedObject; + + private SaslServer _saslServer; + + private Object _lastReceived; + + private Object _lastSent; + + protected boolean _closed; + // maximum number of channels this session should have + private long _maxNoOfChannels = 1000; + + /* AMQP Version for this session */ + private ProtocolVersion _protocolVersion = ProtocolVersion.getLatestSupportedVersion(); + + private FieldTable _clientProperties; + private final List _taskList = new CopyOnWriteArrayList(); + + private List _closingChannelsList = new CopyOnWriteArrayList(); + private ProtocolOutputConverter _protocolOutputConverter; + private Principal _authorizedID; + private MethodDispatcher _dispatcher; + private ProtocolSessionIdentifier _sessionIdentifier; + + private static final long LAST_WRITE_FUTURE_JOIN_TIMEOUT = 60000L; + private org.apache.mina.common.WriteFuture _lastWriteFuture; + + public ManagedObject getManagedObject() + { + return _managedObject; + } + + public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory) + throws AMQException + { + _stateManager = new AMQStateManager(virtualHostRegistry, this); + _minaProtocolSession = session; + session.setAttachment(this); + + _codecFactory = codecFactory; + + try + { + IoServiceConfig config = session.getServiceConfig(); + ReadWriteThreadModel threadModel = (ReadWriteThreadModel) config.getThreadModel(); + threadModel.getAsynchronousReadFilter().createNewJobForSession(session); + threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); + } + catch (RuntimeException e) + { + e.printStackTrace(); + throw e; + + } + } + + public AMQMinaProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory, + AMQStateManager stateManager) throws AMQException + { + _stateManager = stateManager; + _minaProtocolSession = session; + session.setAttachment(this); + + _codecFactory = codecFactory; + + } + + private AMQProtocolSessionMBean createMBean() throws AMQException + { + try + { + return new AMQProtocolSessionMBean(this); + } + catch (JMException ex) + { + _logger.error("AMQProtocolSession MBean creation has failed ", ex); + throw new AMQException("AMQProtocolSession MBean creation has failed ", ex); + } + } + + public IoSession getIOSession() + { + return _minaProtocolSession; + } + + public static AMQProtocolSession getAMQProtocolSession(IoSession minaProtocolSession) + { + return (AMQProtocolSession) minaProtocolSession.getAttachment(); + } + + public void dataBlockReceived(AMQDataBlock message) throws Exception + { + _lastReceived = message; + if (message instanceof ProtocolInitiation) + { + protocolInitiationReceived((ProtocolInitiation) message); + + } + else if (message instanceof AMQFrame) + { + AMQFrame frame = (AMQFrame) message; + frameReceived(frame); + + } + else + { + throw new UnknnownMessageTypeException(message); + } + } + + private void frameReceived(AMQFrame frame) throws AMQException + { + int channelId = frame.getChannel(); + AMQBody body = frame.getBodyFrame(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Frame Received: " + frame); + } + + // Check that this channel is not closing + if (channelAwaitingClosure(channelId)) + { + if ((frame.getBodyFrame() instanceof ChannelCloseOkBody)) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok"); + } + } + else + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure. Should close socket as client did not close-ok :" + frame); + } + + closeProtocolSession(); + return; + } + } + + try + { + body.handle(channelId, this); + } + catch (AMQException e) + { + closeChannel(channelId); + throw e; + } + + } + + private void protocolInitiationReceived(ProtocolInitiation pi) + { + // this ensures the codec never checks for a PI message again + ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false); + try + { + ProtocolVersion pv = pi.checkVersion(); // Fails if not correct + + // This sets the protocol version (and hence framing classes) for this session. + setProtocolVersion(pv); + + String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms(); + + String locales = "en_US"; + + AMQMethodBody responseBody = getMethodRegistry().createConnectionStartBody((short) getProtocolMajorVersion(), + (short) getProtocolMinorVersion(), + null, + mechanisms.getBytes(), + locales.getBytes()); + _minaProtocolSession.write(responseBody.generateFrame(0)); + + } + catch (AMQException e) + { + _logger.info("Received unsupported protocol initiation for protocol version: " + getProtocolVersion()); + + _minaProtocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); + + // TODO: Close connection (but how to wait until message is sent?) + // ritchiem 2006-12-04 will this not do? + // WriteFuture future = _minaProtocolSession.write(new ProtocolInitiation(pv[i][PROTOCOLgetProtocolMajorVersion()], pv[i][PROTOCOLgetProtocolMinorVersion()])); + // future.join(); + // close connection + + } + } + + public void methodFrameReceived(int channelId, AMQMethodBody methodBody) + { + + final AMQMethodEvent evt = new AMQMethodEvent(channelId, methodBody); + + try + { + try + { + + boolean wasAnyoneInterested = _stateManager.methodReceived(evt); + + if (!_frameListeners.isEmpty()) + { + for (AMQMethodListener listener : _frameListeners) + { + wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; + } + } + + if (!wasAnyoneInterested) + { + throw new AMQNoMethodHandlerException(evt); + } + } + catch (AMQChannelException e) + { + if (getChannel(channelId) != null) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing channel due to: " + e.getMessage()); + } + + writeFrame(e.getCloseFrame(channelId)); + closeChannel(channelId); + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("ChannelException occured on non-existent channel:" + e.getMessage()); + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e.getMessage()); + } + + AMQConnectionException ce = + evt.getMethod().getConnectionException(AMQConstant.CHANNEL_ERROR, + AMQConstant.CHANNEL_ERROR.getName().toString()); + + closeConnection(channelId, ce, false); + } + } + catch (AMQConnectionException e) + { + closeConnection(channelId, e, false); + } + } + catch (Exception e) + { + + for (AMQMethodListener listener : _frameListeners) + { + listener.error(e); + } + + _logger.error("Unexpected exception while processing frame. Closing connection.", e); + + closeProtocolSession(); + } + } + + public void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException + { + + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentHeader(body); + + } + + public void contentBodyReceived(int channelId, ContentBody body) throws AMQException + { + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentBody(body); + } + + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) + { + // NO - OP + } + + /** + * Convenience method that writes a frame to the protocol session. Equivalent to calling + * getProtocolSession().write(). + * + * @param frame the frame to write + */ + public void writeFrame(AMQDataBlock frame) + { + _lastSent = frame; + + _lastWriteFuture = _minaProtocolSession.write(frame); + } + + public AMQShortString getContextKey() + { + return _contextKey; + } + + public void setContextKey(AMQShortString contextKey) + { + _contextKey = contextKey; + } + + public List getChannels() + { + return new ArrayList(_channelMap.values()); + } + + public AMQChannel getAndAssertChannel(int channelId) throws AMQException + { + AMQChannel channel = getChannel(channelId); + if (channel == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "Channel not found with id:" + channelId); + } + + return channel; + } + + public AMQChannel getChannel(int channelId) throws AMQException + { + final AMQChannel channel = + ((channelId & CHANNEL_CACHE_SIZE) == channelId) ? _cachedChannels[channelId] : _channelMap.get(channelId); + if ((channel == null) || channel.isClosing()) + { + return null; + } + else + { + return channel; + } + } + + public boolean channelAwaitingClosure(int channelId) + { + return !_closingChannelsList.isEmpty() && _closingChannelsList.contains(channelId); + } + + public void addChannel(AMQChannel channel) throws AMQException + { + if (_closed) + { + throw new AMQException("Session is closed"); + } + + final int channelId = channel.getChannelId(); + + if (_closingChannelsList.contains(channelId)) + { + throw new AMQException("Session is marked awaiting channel close"); + } + + if (_channelMap.size() == _maxNoOfChannels) + { + String errorMessage = + toString() + ": maximum number of channels has been reached (" + _maxNoOfChannels + + "); can't create channel"; + _logger.error(errorMessage); + throw new AMQException(AMQConstant.NOT_ALLOWED, errorMessage); + } + else + { + _channelMap.put(channel.getChannelId(), channel); + } + + if (((channelId & CHANNEL_CACHE_SIZE) == channelId)) + { + _cachedChannels[channelId] = channel; + } + + checkForNotification(); + } + + private void checkForNotification() + { + int channelsCount = _channelMap.size(); + if (channelsCount >= _maxNoOfChannels) + { + _managedObject.notifyClients("Channel count (" + channelsCount + ") has reached the threshold value"); + } + } + + public Long getMaximumNumberOfChannels() + { + return _maxNoOfChannels; + } + + public void setMaximumNumberOfChannels(Long value) + { + _maxNoOfChannels = value; + } + + public void commitTransactions(AMQChannel channel) throws AMQException + { + if ((channel != null) && channel.isTransactional()) + { + channel.commit(); + } + } + + public void rollbackTransactions(AMQChannel channel) throws AMQException + { + if ((channel != null) && channel.isTransactional()) + { + channel.rollback(); + } + } + + /** + * Close a specific channel. This will remove any resources used by the channel, including:

  • any queue + * subscriptions (this may in turn remove queues if they are auto delete
+ * + * @param channelId id of the channel to close + * + * @throws AMQException if an error occurs closing the channel + * @throws IllegalArgumentException if the channel id is not valid + */ + public void closeChannel(int channelId) throws AMQException + { + final AMQChannel channel = getChannel(channelId); + if (channel == null) + { + throw new IllegalArgumentException("Unknown channel id"); + } + else + { + try + { + channel.close(); + markChannelAwaitingCloseOk(channelId); + } + finally + { + removeChannel(channelId); + } + } + } + + public void closeChannelOk(int channelId) + { + // todo QPID-847 - This is called from two lcoations ChannelCloseHandler and ChannelCloseOkHandler. + // When it is the CC_OK_Handler then it makes sence to remove the channel else we will leak memory. + // We do it from the Close Handler as we are sending the OK back to the client. + // While this is AMQP spec compliant. The Java client in the event of an IllegalArgumentException + // will send a close-ok.. Where we should call removeChannel. + // However, due to the poor exception handling on the client. The client-user will be notified of the + // InvalidArgument and if they then decide to close the session/connection then the there will be time + // for that to occur i.e. a new close method be sent before the exeption handling can mark the session closed. + //removeChannel(channelId); + _closingChannelsList.remove(new Integer(channelId)); + } + + private void markChannelAwaitingCloseOk(int channelId) + { + _closingChannelsList.add(channelId); + } + + /** + * In our current implementation this is used by the clustering code. + * + * @param channelId The channel to remove + */ + public void removeChannel(int channelId) + { + _channelMap.remove(channelId); + if ((channelId & CHANNEL_CACHE_SIZE) == channelId) + { + _cachedChannels[channelId] = null; + } + } + + /** + * Initialise heartbeats on the session. + * + * @param delay delay in seconds (not ms) + */ + public void initHeartbeats(int delay) + { + if (delay > 0) + { + _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay); + _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.getInstance().getTimeout(delay)); + } + } + + /** + * Closes all channels that were opened by this protocol session. This frees up all resources used by the channel. + * + * @throws AMQException if an error occurs while closing any channel + */ + private void closeAllChannels() throws AMQException + { + for (AMQChannel channel : _channelMap.values()) + { + channel.close(); + } + + _channelMap.clear(); + for (int i = 0; i <= CHANNEL_CACHE_SIZE; i++) + { + _cachedChannels[i] = null; + } + } + + /** This must be called when the session is _closed in order to free up any resources managed by the session. */ + public void closeSession() throws AMQException + { + if (!_closed) + { + _closed = true; + + if (_virtualHost != null) + { + _virtualHost.getConnectionRegistry().deregisterConnection(this); + } + + closeAllChannels(); + if (_managedObject != null) + { + _managedObject.unregister(); + } + + for (Task task : _taskList) + { + task.doTask(this); + } + } + } + + public void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e.getMessage()); + } + + markChannelAwaitingCloseOk(channelId); + closeSession(); + _stateManager.changeState(AMQState.CONNECTION_CLOSING); + writeFrame(e.getCloseFrame(channelId)); + + if (closeProtocolSession) + { + closeProtocolSession(); + } + } + + public void closeProtocolSession() + { + closeProtocolSession(true); + } + + public void closeProtocolSession(boolean waitLast) + { + if (waitLast && (_lastWriteFuture != null)) + { + _logger.debug("Waiting for last write to join."); + _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + } + + _logger.debug("REALLY Closing protocol session:" + _minaProtocolSession); + final CloseFuture future = _minaProtocolSession.close(); + future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + + try + { + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + catch (AMQException e) + { + _logger.info(e.getMessage()); + } + } + + public String toString() + { + return _minaProtocolSession.getRemoteAddress() + "(" + (getAuthorizedID() == null ? "?" : getAuthorizedID().getName() + ")"); + } + + public String dump() + { + return this + " last_sent=" + _lastSent + " last_received=" + _lastReceived; + } + + /** @return an object that can be used to identity */ + public Object getKey() + { + return _minaProtocolSession.getRemoteAddress(); + } + + /** + * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may + * be bound to multiple addresses this could vary depending on the acceptor this session was created from. + * + * @return a String FQDN + */ + public String getLocalFQDN() + { + SocketAddress address = _minaProtocolSession.getLocalAddress(); + // we use the vmpipe address in some tests hence the need for this rather ugly test. The host + // information is used by SASL primary. + if (address instanceof InetSocketAddress) + { + return ((InetSocketAddress) address).getHostName(); + } + else if (address instanceof VmPipeAddress) + { + return "vmpipe:" + ((VmPipeAddress) address).getPort(); + } + else + { + throw new IllegalArgumentException("Unsupported socket address class: " + address); + } + } + + public SaslServer getSaslServer() + { + return _saslServer; + } + + public void setSaslServer(SaslServer saslServer) + { + _saslServer = saslServer; + } + + public FieldTable getClientProperties() + { + return _clientProperties; + } + + public void setClientProperties(FieldTable clientProperties) + { + _clientProperties = clientProperties; + if (_clientProperties != null) + { + if (_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE) != null) + { + setContextKey(new AMQShortString(_clientProperties.getString(CLIENT_PROPERTIES_INSTANCE))); + } + + if (_clientProperties.getString(ClientProperties.version.toString()) != null) + { + _clientVersion = new AMQShortString(_clientProperties.getString(ClientProperties.version.toString())); + } + } + _sessionIdentifier = new ProtocolSessionIdentifier(this); + } + + private void setProtocolVersion(ProtocolVersion pv) + { + _protocolVersion = pv; + + _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); + _dispatcher = ServerMethodDispatcherImpl.createMethodDispatcher(_stateManager, _protocolVersion); + } + + public byte getProtocolMajorVersion() + { + return _protocolVersion.getMajorVersion(); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolVersion; + } + + public byte getProtocolMinorVersion() + { + return _protocolVersion.getMinorVersion(); + } + + public boolean isProtocolVersion(byte major, byte minor) + { + return (getProtocolMajorVersion() == major) && (getProtocolMinorVersion() == minor); + } + + public MethodRegistry getRegistry() + { + return getMethodRegistry(); + } + + public Object getClientIdentifier() + { + return (_minaProtocolSession != null) ? _minaProtocolSession.getRemoteAddress() : null; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public void setVirtualHost(VirtualHost virtualHost) throws AMQException + { + _virtualHost = virtualHost; + + _virtualHost.getConnectionRegistry().registerConnection(this); + + _managedObject = createMBean(); + _managedObject.register(); + } + + public void addSessionCloseTask(Task task) + { + _taskList.add(task); + } + + public void removeSessionCloseTask(Task task) + { + _taskList.remove(task); + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return _protocolOutputConverter; + } + + public void setAuthorizedID(Principal authorizedID) + { + _authorizedID = authorizedID; + } + + public Principal getAuthorizedID() + { + return _authorizedID; + } + + public MethodRegistry getMethodRegistry() + { + return MethodRegistry.getMethodRegistry(getProtocolVersion()); + } + + public MethodDispatcher getMethodDispatcher() + { + return _dispatcher; + } + + public ProtocolSessionIdentifier getSessionIdentifier() + { + return _sessionIdentifier; + } + + public String getClientVersion() + { + return (_clientVersion == null) ? null : _clientVersion.toString(); + } + + public void setSender(Sender sender) + { + // No-op, interface munging between this and AMQProtocolSession + } + + public void init() + { + // No-op, interface munging between this and AMQProtocolSession + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java new file mode 100644 index 0000000000..a7599a3e0d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.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.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +/** + * AMQNoMethodHandlerException represents the case where no method handler exists to handle an AQMP method. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to handle an AMQP method. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Missing method handler. Unlikely to ever happen, and if it does its a coding error. Consider replacing with a + * Runtime. + */ +public class AMQNoMethodHandlerException extends AMQException +{ + public AMQNoMethodHandlerException(AMQMethodEvent evt) + { + super("AMQMethodEvent " + evt + " was not processed by any listener on Broker."); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java new file mode 100644 index 0000000000..d8dbf97e49 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java @@ -0,0 +1,280 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.mina.common.ByteBuffer; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.ReadThrottleFilterBuilder; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import org.apache.mina.filter.codec.QpidProtocolCodecFilter; +import org.apache.mina.filter.executor.ExecutorFilter; +import org.apache.mina.util.SessionUtil; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.transport.ConnectorConfiguration; +import org.apache.qpid.ssl.SSLContextFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** + * The protocol handler handles "protocol events" for all connections. The state + * associated with an individual connection is accessed through the protocol session. + * + * We delegate all frame (message) processing to the AMQProtocolSession which wraps + * the state for the connection. + */ +public class AMQPFastProtocolHandler extends IoHandlerAdapter +{ + private static final Logger _logger = Logger.getLogger(AMQPFastProtocolHandler.class); + + private final IApplicationRegistry _applicationRegistry; + + private static String DEFAULT_BUFFER_READ_LIMIT_SIZE = "262144"; + private static String DEFAULT_BUFFER_WRITE_LIMIT_SIZE = "262144"; + + private final int BUFFER_READ_LIMIT_SIZE; + private final int BUFFER_WRITE_LIMIT_SIZE; + + public AMQPFastProtocolHandler(Integer applicationRegistryInstance) + { + this(ApplicationRegistry.getInstance(applicationRegistryInstance)); + } + + public AMQPFastProtocolHandler(IApplicationRegistry applicationRegistry) + { + _applicationRegistry = applicationRegistry; + + // Read the configuration from the application registry + BUFFER_READ_LIMIT_SIZE = Integer.parseInt(_applicationRegistry.getConfiguration().getString("broker.connector.protectio.readBufferLimitSize", DEFAULT_BUFFER_READ_LIMIT_SIZE)); + BUFFER_WRITE_LIMIT_SIZE = Integer.parseInt(_applicationRegistry.getConfiguration().getString("broker.connector.protectio.writeBufferLimitSize", DEFAULT_BUFFER_WRITE_LIMIT_SIZE)); + + _logger.debug("AMQPFastProtocolHandler created"); + } + + protected AMQPFastProtocolHandler(AMQPFastProtocolHandler handler) + { + this(handler._applicationRegistry); + } + + public void sessionCreated(IoSession protocolSession) throws Exception + { + SessionUtil.initialize(protocolSession); + final AMQCodecFactory codecFactory = new AMQCodecFactory(true); + + createSession(protocolSession, _applicationRegistry, codecFactory); + _logger.info("Protocol session created for:" + protocolSession.getRemoteAddress()); + + final QpidProtocolCodecFilter pcf = new QpidProtocolCodecFilter(codecFactory); + + ConnectorConfiguration connectorConfig = ApplicationRegistry.getInstance(). + getConfiguredObject(ConnectorConfiguration.class); + if (connectorConfig.enableExecutorPool) + { + if (connectorConfig.enableSSL && isSSLClient(connectorConfig, protocolSession)) + { + String keystorePath = connectorConfig.keystorePath; + String keystorePassword = connectorConfig.keystorePassword; + String certType = connectorConfig.certType; + SSLContextFactory sslContextFactory = new SSLContextFactory(keystorePath, keystorePassword, certType); + protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter", + new SSLFilter(sslContextFactory.buildServerContext())); + } + protocolSession.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf); + } + else + { + protocolSession.getFilterChain().addLast("protocolFilter", pcf); + if (connectorConfig.enableSSL && isSSLClient(connectorConfig, protocolSession)) + { + String keystorePath = connectorConfig.keystorePath; + String keystorePassword = connectorConfig.keystorePassword; + String certType = connectorConfig.certType; + SSLContextFactory sslContextFactory = new SSLContextFactory(keystorePath, keystorePassword, certType); + protocolSession.getFilterChain().addBefore("protocolFilter", "sslFilter", + new SSLFilter(sslContextFactory.buildServerContext())); + } + + } + + if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("broker.connector.protectio.enabled", false)) + { + try + { +// //Add IO Protection Filters + IoFilterChain chain = protocolSession.getFilterChain(); + + + protocolSession.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(BUFFER_READ_LIMIT_SIZE); + readfilter.attach(chain); + + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(BUFFER_WRITE_LIMIT_SIZE); + writefilter.attach(chain); + + protocolSession.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + _logger.info("Using IO Read/Write Filter Protection"); + } + catch (Exception e) + { + _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage()); + } + } + } + + /** Separated into its own, protected, method to allow easier reuse */ + protected void createSession(IoSession session, IApplicationRegistry applicationRegistry, AMQCodecFactory codec) throws AMQException + { + new AMQMinaProtocolSession(session, applicationRegistry.getVirtualHostRegistry(), codec); + } + + public void sessionOpened(IoSession protocolSession) throws Exception + { + _logger.info("Session opened for:" + protocolSession.getRemoteAddress()); + } + + public void sessionClosed(IoSession protocolSession) throws Exception + { + _logger.info("Protocol Session closed for:" + protocolSession.getRemoteAddress()); + final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); + //fixme -- this can be null + if (amqProtocolSession != null) + { + try + { + amqProtocolSession.closeSession(); + } + catch (AMQException e) + { + _logger.error("Caught AMQException whilst closingSession:" + e); + } + } + } + + public void sessionIdle(IoSession session, IdleStatus status) throws Exception + { + _logger.debug("Protocol Session [" + this + "] idle: " + status + " :for:" + session.getRemoteAddress()); + if (IdleStatus.WRITER_IDLE.equals(status)) + { + //write heartbeat frame: + session.write(HeartbeatBody.FRAME); + } + else if (IdleStatus.READER_IDLE.equals(status)) + { + //failover: + throw new IOException("Timed out while waiting for heartbeat from peer."); + } + + } + + public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception + { + AMQProtocolSession session = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); + if (throwable instanceof AMQProtocolHeaderException) + { + + protocolSession.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); + + protocolSession.close(); + + _logger.error("Error in protocol initiation " + session + ":" + protocolSession.getRemoteAddress() + " :" + throwable.getMessage(), throwable); + } + else if (throwable instanceof IOException) + { + _logger.error("IOException caught in" + session + ", session closed implictly: " + throwable); + } + else + { + _logger.error("Exception caught in" + session + ", closing session explictly: " + throwable, throwable); + + + MethodRegistry methodRegistry = MethodRegistry.getMethodRegistry(session.getProtocolVersion()); + ConnectionCloseBody closeBody = methodRegistry.createConnectionCloseBody(200,new AMQShortString(throwable.getMessage()),0,0); + + protocolSession.write(closeBody.generateFrame(0)); + + protocolSession.close(); + } + } + + /** + * Invoked when a message is received on a particular protocol session. Note that a + * protocol session is directly tied to a particular physical connection. + * + * @param protocolSession the protocol session that received the message + * @param message the message itself (i.e. a decoded frame) + * + * @throws Exception if the message cannot be processed + */ + public void messageReceived(IoSession protocolSession, Object message) throws Exception + { + final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession); + + if (message instanceof AMQDataBlock) + { + amqProtocolSession.dataBlockReceived((AMQDataBlock) message); + + } + else if (message instanceof ByteBuffer) + { + throw new IllegalStateException("Handed undecoded ByteBuffer buf = " + message); + } + else + { + throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message); + } + } + + /** + * Called after a message has been sent out on a particular protocol session + * + * @param protocolSession the protocol session (i.e. connection) on which this + * message was sent + * @param object the message (frame) that was encoded and sent + * + * @throws Exception if we want to indicate an error + */ + public void messageSent(IoSession protocolSession, Object object) throws Exception + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Message sent: " + object); + } + } + + protected boolean isSSLClient(ConnectorConfiguration connectionConfig, + IoSession protocolSession) + { + InetSocketAddress addr = (InetSocketAddress) protocolSession.getLocalAddress(); + return addr.getPort() == connectionConfig.sslPort; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java new file mode 100644 index 0000000000..07c153bfe8 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.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.protocol; + +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; + +/** + * The protocol provide's role is to encapsulate the initialisation of the protocol handler. + * + * The protocol handler (see AMQPFastProtocolHandler class) handles protocol events + * such as connection closing or a frame being received. It can either do this directly + * or pass off to the protocol session in the cases where state information is required to + * deal with the event. + * + */ +public class AMQPProtocolProvider +{ + /** + * Handler for protocol events + */ + private AMQPFastProtocolHandler _handler; + + public AMQPProtocolProvider() + { + IApplicationRegistry registry = ApplicationRegistry.getInstance(); + _handler = new AMQPFastProtocolHandler(registry); + } + + public AMQPFastProtocolHandler getHandler() + { + return _handler; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java new file mode 100644 index 0000000000..1bac601225 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.security.sasl.SaslServer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.security.Principal; + + +public interface AMQProtocolSession extends AMQVersionAwareProtocolSession +{ + + public static final class ProtocolSessionIdentifier + { + private final Object _sessionIdentifier; + private final Object _sessionInstance; + + ProtocolSessionIdentifier(AMQProtocolSession session) + { + _sessionIdentifier = session.getClientIdentifier(); + _sessionInstance = session.getClientProperties() == null ? null : session.getClientProperties().getObject(ClientProperties.instance.toAMQShortString()); + } + + public Object getSessionIdentifier() + { + return _sessionIdentifier; + } + + public Object getSessionInstance() + { + return _sessionInstance; + } + } + + public static interface Task + { + public void doTask(AMQProtocolSession session) throws AMQException; + } + + /** + * Called when a protocol data block is received + * + * @param message the data block that has been received + * + * @throws Exception if processing the datablock fails + */ + void dataBlockReceived(AMQDataBlock message) throws Exception; + + /** + * Get the context key associated with this session. Context key is described in the AMQ protocol specification (RFC + * 6). + * + * @return the context key + */ + AMQShortString getContextKey(); + + /** + * Set the context key associated with this session. Context key is described in the AMQ protocol specification (RFC + * 6). + * + * @param contextKey the context key + */ + void setContextKey(AMQShortString contextKey); + + /** + * Get the channel for this session associated with the specified id. A channel id is unique per connection (i.e. + * per session). + * + * @param channelId the channel id which must be valid + * + * @return null if no channel exists, the channel otherwise + */ + AMQChannel getChannel(int channelId) throws AMQException; + + /** + * Associate a channel with this session. + * + * @param channel the channel to associate with this session. It is an error to associate the same channel with more + * than one session but this is not validated. + */ + void addChannel(AMQChannel channel) throws AMQException; + + /** + * Close a specific channel. This will remove any resources used by the channel, including:

  • any queue + * subscriptions (this may in turn remove queues if they are auto delete
+ * + * @param channelId id of the channel to close + * + * @throws org.apache.qpid.AMQException if an error occurs closing the channel + * @throws IllegalArgumentException if the channel id is not valid + */ + void closeChannel(int channelId) throws AMQException; + + /** + * Markes the specific channel as closed. This will release the lock for that channel id so a new channel can be + * created on that id. + * + * @param channelId id of the channel to close + */ + void closeChannelOk(int channelId); + + /** + * Check to see if this chanel is closing + * + * @param channelId id to check + * @return boolean with state of channel awaiting closure + */ + boolean channelAwaitingClosure(int channelId); + + /** + * Remove a channel from the session but do not close it. + * + * @param channelId + */ + void removeChannel(int channelId); + + /** + * Initialise heartbeats on the session. + * + * @param delay delay in seconds (not ms) + */ + void initHeartbeats(int delay); + + /** This must be called when the session is _closed in order to free up any resources managed by the session. */ + void closeSession() throws AMQException; + + /** This must be called to close the session in order to free up any resources managed by the session. */ + void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException; + + + /** @return a key that uniquely identifies this session */ + Object getKey(); + + /** + * Get the fully qualified domain name of the local address to which this session is bound. Since some servers may + * be bound to multiple addresses this could vary depending on the acceptor this session was created from. + * + * @return a String FQDN + */ + String getLocalFQDN(); + + /** @return the sasl server that can perform authentication for this session. */ + SaslServer getSaslServer(); + + /** + * Set the sasl server that is to perform authentication for this session. + * + * @param saslServer + */ + void setSaslServer(SaslServer saslServer); + + + FieldTable getClientProperties(); + + void setClientProperties(FieldTable clientProperties); + + Object getClientIdentifier(); + + VirtualHost getVirtualHost(); + + void setVirtualHost(VirtualHost virtualHost) throws AMQException; + + void addSessionCloseTask(Task task); + + void removeSessionCloseTask(Task task); + + public ProtocolOutputConverter getProtocolOutputConverter(); + + void setAuthorizedID(Principal authorizedID); + + /** @return a Principal that was used to authorized this session */ + Principal getAuthorizedID(); + + public MethodRegistry getMethodRegistry(); + + public MethodDispatcher getMethodDispatcher(); + + public ProtocolSessionIdentifier getSessionIdentifier(); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java new file mode 100644 index 0000000000..bd072985c4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -0,0 +1,306 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 java.security.Principal; +import java.util.Date; +import java.util.List; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.monitor.MonitorNotification; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.management.ManagedObject; + +/** + * This MBean class implements the management interface. In order to make more attributes, operations and notifications + * available over JMX simply augment the ManagedConnection interface and add the appropriate implementation here. + */ +@MBeanDescription("Management Bean for an AMQ Broker Connection") +public class AMQProtocolSessionMBean extends AMQManagedObject implements ManagedConnection +{ + private AMQMinaProtocolSession _session = null; + private String _name = null; + + // openmbean data types for representing the channel attributes + private static final String[] _channelAtttibuteNames = + { "Channel Id", "Transactional", "Default Queue", "Unacknowledged Message Count" }; + private static final String[] _indexNames = { _channelAtttibuteNames[0] }; + private static final OpenType[] _channelAttributeTypes = + { SimpleType.INTEGER, SimpleType.BOOLEAN, SimpleType.STRING, SimpleType.INTEGER }; + private static CompositeType _channelType = null; // represents the data type for channel data + private static TabularType _channelsType = null; // Data type for list of channels type + private static final AMQShortString BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION = + new AMQShortString("Broker Management Console has closed the connection."); + + @MBeanConstructor("Creates an MBean exposing an AMQ Broker Connection") + public AMQProtocolSessionMBean(AMQMinaProtocolSession session) throws NotCompliantMBeanException, OpenDataException + { + super(ManagedConnection.class, ManagedConnection.TYPE); + _session = session; + String remote = getRemoteAddress(); + remote = "anonymous".equals(remote) ? (remote + hashCode()) : remote; + _name = jmxEncode(new StringBuffer(remote), 0).toString(); + init(); + } + + static + { + try + { + init(); + } + catch (JMException ex) + { + // This is not expected to ever occur. + throw new RuntimeException("Got JMException in static initializer.", ex); + } + } + + /** + * initialises the openmbean data types + */ + private static void init() throws OpenDataException + { + _channelType = + new CompositeType("Channel", "Channel Details", _channelAtttibuteNames, _channelAtttibuteNames, + _channelAttributeTypes); + _channelsType = new TabularType("Channels", "Channels", _channelType, _indexNames); + } + + public String getClientId() + { + return (_session.getContextKey() == null) ? null : _session.getContextKey().toString(); + } + + public String getAuthorizedId() + { + return (_session.getAuthorizedID() != null ) ? _session.getAuthorizedID().getName() : null; + } + + public String getVersion() + { + return (_session.getClientVersion() == null) ? null : _session.getClientVersion().toString(); + } + + public Date getLastIoTime() + { + return new Date(_session.getIOSession().getLastIoTime()); + } + + public String getRemoteAddress() + { + return _session.getIOSession().getRemoteAddress().toString(); + } + + public ManagedObject getParentObject() + { + return _session.getVirtualHost().getManagedObject(); + } + + public Long getWrittenBytes() + { + return _session.getIOSession().getWrittenBytes(); + } + + public Long getReadBytes() + { + return _session.getIOSession().getReadBytes(); + } + + public Long getMaximumNumberOfChannels() + { + return _session.getMaximumNumberOfChannels(); + } + + public void setMaximumNumberOfChannels(Long value) + { + _session.setMaximumNumberOfChannels(value); + } + + public String getObjectInstanceName() + { + return _name; + } + + /** + * 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 + { + try + { + AMQChannel channel = _session.getChannel(channelId); + if (channel == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + + _session.commitTransactions(channel); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * 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 + { + try + { + AMQChannel channel = _session.getChannel(channelId); + if (channel == null) + { + throw new JMException("The channel (channel Id = " + channelId + ") does not exist"); + } + + _session.rollbackTransactions(channel); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * 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 = _session.getChannels(); + + for (AMQChannel channel : list) + { + Object[] itemValues = + { + channel.getChannelId(), channel.isTransactional(), + (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getName().asString() : null, + channel.getUnacknowledgedMessageMap().size() + }; + + CompositeData channelData = new CompositeDataSupport(_channelType, _channelAtttibuteNames, 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 = _session.getMethodRegistry(); + ConnectionCloseBody responseBody = + methodRegistry.createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), + // replyCode + BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION, + // replyText, + 0, + 0); + + _session.writeFrame(responseBody.generateFrame(0)); + + try + { + _session.closeSession(); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + @Override + public MBeanNotificationInfo[] getNotificationInfo() + { + String[] notificationTypes = new String[] { MonitorNotification.THRESHOLD_VALUE_EXCEEDED }; + String name = MonitorNotification.class.getName(); + String description = "Channel count has reached threshold value"; + MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes, name, description); + + return new MBeanNotificationInfo[] { info1 }; + } + + public void notifyClients(String notificationMsg) + { + Notification n = + new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, + System.currentTimeMillis(), notificationMsg); + _broadcaster.sendNotification(n); + } + +} // End of MBean class diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java new file mode 100644 index 0000000000..2abcecb6de --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java @@ -0,0 +1,51 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +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; + +public class ExchangeInitialiser +{ + public void initialise(ExchangeFactory factory, ExchangeRegistry registry) throws AMQException{ + for (ExchangeType type : factory.getRegisteredTypes()) + { + define (registry, factory, type.getDefaultExchangeName(), type.getName()); + } + + define(registry, factory, ExchangeDefaults.DEFAULT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); + registry.setDefaultExchange(registry.getExchange(ExchangeDefaults.DEFAULT_EXCHANGE_NAME)); + } + + private void define(ExchangeRegistry r, ExchangeFactory f, + AMQShortString name, AMQShortString type) throws AMQException + { + if(r.getExchange(name)== null) + { + r.registerExchange(f.createExchange(name, type, true, false, 0)); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java new file mode 100644 index 0000000000..310deaaf55 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java @@ -0,0 +1,67 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration.Configured; +import org.apache.qpid.server.registry.ApplicationRegistry; + +public class HeartbeatConfig +{ + @Configured(path = "heartbeat.delay", defaultValue = "5") + public int delay = 5;//in secs + @Configured(path = "heartbeat.timeoutFactor", defaultValue = "2.0") + public double timeoutFactor = 2; + + public double getTimeoutFactor() + { + return timeoutFactor; + } + + public void setTimeoutFactor(double timeoutFactor) + { + this.timeoutFactor = timeoutFactor; + } + + public int getDelay() + { + return delay; + } + + public void setDelay(int delay) + { + this.delay = delay; + } + + int getTimeout(int writeDelay) + { + return (int) (timeoutFactor * writeDelay); + } + + public static HeartbeatConfig getInstance() + { + return ApplicationRegistry.getInstance().getConfiguredObject(HeartbeatConfig.class); + } + + public String toString() + { + return "HeartBeatConfig{delay = " + delay + " timeoutFactor = " + timeoutFactor + "}"; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java new file mode 100644 index 0000000000..e6e713ac6d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java @@ -0,0 +1,135 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.protocol; + +import java.io.IOException; +import java.util.Date; +import java.security.Principal; + +import javax.management.JMException; +import javax.management.MBeanOperationInfo; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.server.management.MBeanAttribute; +import org.apache.qpid.server.management.MBeanOperation; +import org.apache.qpid.server.management.MBeanOperationParameter; + +/** + * The management interface exposed to allow management of Connections. + * @author Bhupendra Bhardwaj + * @version 0.1 + */ +public interface ManagedConnection +{ + static final String TYPE = "Connection"; + + @MBeanAttribute(name = "ClientId", description = "Client Id") + String getClientId(); + + @MBeanAttribute(name = "AuthorizedId", description = "User Name") + String getAuthorizedId(); + + @MBeanAttribute(name = "Version", description = "Client Version") + String getVersion(); + + /** + * Tells the remote address of this connection. + * @return remote address + */ + @MBeanAttribute(name="RemoteAddress", description=TYPE + " Address") + String getRemoteAddress(); + + /** + * Tells the last time, the IO operation was done. + * @return last IO time. + */ + @MBeanAttribute(name="LastIOTime", description="The last time, the IO operation was done") + Date getLastIoTime(); + + /** + * Tells the total number of bytes written till now. + * @return number of bytes written. + * + @MBeanAttribute(name="WrittenBytes", description="The total number of bytes written till now") + Long getWrittenBytes(); + */ + /** + * Tells the total number of bytes read till now. + * @return number of bytes read. + * + @MBeanAttribute(name="ReadBytes", description="The total number of bytes read till now") + Long getReadBytes(); + */ + + /** + * Threshold high value for no of channels. This is useful in setting notifications or + * taking required action is there are more channels being created. + * @return threshold limit for no of channels + */ + Long getMaximumNumberOfChannels(); + + /** + * Sets the threshold high value for number of channels for a connection + * @param value + */ + @MBeanAttribute(name="MaximumNumberOfChannels", description="The threshold high value for number of channels for this connection") + void setMaximumNumberOfChannels(Long value); + + //********** Operations *****************// + + /** + * channel details of all the channels opened for this connection. + * @return general channel details + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="channels", description="Channel details for this connection") + TabularData channels() throws IOException, JMException; + + /** + * Commits the transactions if the channel is transactional. + * @param channelId + * @throws JMException + */ + @MBeanOperation(name="commitTransaction", + description="Commits the transactions for given channel Id, if the channel is transactional", + impact= MBeanOperationInfo.ACTION) + void commitTransactions(@MBeanOperationParameter(name="channel Id", description="channel Id")int channelId) throws JMException; + + /** + * Rollsback the transactions if the channel is transactional. + * @param channelId + * @throws JMException + */ + @MBeanOperation(name="rollbackTransactions", + description="Rollsback the transactions for given channel Id, if the channel is transactional", + impact= MBeanOperationInfo.ACTION) + void rollbackTransactions(@MBeanOperationParameter(name="channel Id", description="channel Id")int channelId) throws JMException; + + /** + * Closes all the related channels and unregisters this connection from managed objects. + */ + @MBeanOperation(name="closeConnection", + description="Closes this connection and all related channels", + impact= MBeanOperationInfo.ACTION) + void closeConnection() throws Exception; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java new file mode 100644 index 0000000000..6e72aa062f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.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.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; + +/** + * UnknnownMessageTypeException represents a failure when Mina passes an unexpected frame type. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to cast a frame to its expected type. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Seems like this exception was created to handle an unsafe type cast that will never happen in practice. Would + * be better just to leave that as a ClassCastException. However, check the framing layer catches this error + * first. + */ +public class UnknnownMessageTypeException extends AMQException +{ + public UnknnownMessageTypeException(AMQDataBlock message) + { + super("Unknown message type: " + message.getClass().getName() + ": " + message); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java new file mode 100644 index 0000000000..a485649410 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java @@ -0,0 +1,482 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.TransactionalContext; + + +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A deliverable message. + */ +public class AMQMessage implements Filterable +{ + /** Used for debugging purposes. */ + private static final Logger _log = Logger.getLogger(AMQMessage.class); + + private final AtomicInteger _referenceCount = new AtomicInteger(1); + + private final AMQMessageHandle _messageHandle; + + /** Holds the transactional context in which this message is being processed. */ + private StoreContext _storeContext; + + /** Flag to indicate that this message requires 'immediate' delivery. */ + + private static final byte IMMEDIATE = 0x01; + + /** + * Flag to indicate whether this message has been delivered to a consumer. Used in implementing return functionality + * for messages published with the 'immediate' flag. + */ + + private static final byte DELIVERED_TO_CONSUMER = 0x02; + + private byte _flags = 0; + + private long _expiration; + + private final long _size; + + private AMQProtocolSession.ProtocolSessionIdentifier _sessionIdentifier; + private static final byte IMMEDIATE_AND_DELIVERED = (byte) (IMMEDIATE | DELIVERED_TO_CONSUMER); + + + + /** + * Used to iterate through all the body frames associated with this message. Will not keep all the data in memory + * therefore is memory-efficient. + */ + private class BodyFrameIterator implements Iterator + { + private int _channel; + + private int _index = -1; + private AMQProtocolSession _protocolSession; + + private BodyFrameIterator(AMQProtocolSession protocolSession, int channel) + { + _channel = channel; + _protocolSession = protocolSession; + } + + public boolean hasNext() + { + try + { + return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1); + } + catch (AMQException e) + { + _log.error("Unable to get body count: " + e, e); + + return false; + } + } + + public AMQDataBlock next() + { + try + { + + AMQBody cb = + getProtocolVersionMethodConverter().convertToBody(_messageHandle.getContentChunk(getStoreContext(), + ++_index)); + + return new AMQFrame(_channel, cb); + } + catch (AMQException e) + { + // have no choice but to throw a runtime exception + throw new RuntimeException("Error getting content body: " + e, e); + } + + } + + private ProtocolVersionMethodConverter getProtocolVersionMethodConverter() + { + return _protocolSession.getMethodRegistry().getProtocolVersionMethodConverter(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public void clearStoreContext() + { + _storeContext = new StoreContext(); + } + + public StoreContext getStoreContext() + { + return _storeContext; + } + + private class BodyContentIterator implements Iterator + { + + private int _index = -1; + + public boolean hasNext() + { + try + { + return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1); + } + catch (AMQException e) + { + _log.error("Error getting body count: " + e, e); + + return false; + } + } + + public ContentChunk next() + { + try + { + return _messageHandle.getContentChunk(getStoreContext(), ++_index); + } + catch (AMQException e) + { + throw new RuntimeException("Error getting content body: " + e, e); + } + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + + + /** + * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal + * enqueue/routingComplete is not done since the recovery process is responsible for routing the messages to + * queues. + * + * @param messageId + * @param store + * @param factory + * + * @throws AMQException + */ + public AMQMessage(Long messageId, MessageStore store, MessageHandleFactory factory, TransactionalContext txnConext) + throws AMQException + { + _messageHandle = factory.createMessageHandle(messageId, store, true); + _storeContext = txnConext.getStoreContext(); + _size = _messageHandle.getBodySize(txnConext.getStoreContext()); + } + + /** + * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal + * enqueue/routingComplete is not done since the recovery process is responsible for routing the messages to + * queues. + * + * @param messageHandle + * + * @throws AMQException + */ + public AMQMessage( + AMQMessageHandle messageHandle, + StoreContext storeConext, + MessagePublishInfo info) + throws AMQException + { + _messageHandle = messageHandle; + _storeContext = storeConext; + + if(info.isImmediate()) + { + _flags |= IMMEDIATE; + } + _size = messageHandle.getBodySize(storeConext); + + } + + + protected AMQMessage(AMQMessage msg) throws AMQException + { + _messageHandle = msg._messageHandle; + _storeContext = msg._storeContext; + _flags = msg._flags; + _size = msg._size; + + } + + + public String debugIdentity() + { + return "(HC:" + System.identityHashCode(this) + " ID:" + getMessageId() + " Ref:" + _referenceCount.get() + ")"; + } + + public void setExpiration(final long expiration) + { + + _expiration = expiration; + + } + + public boolean isReferenced() + { + return _referenceCount.get() > 0; + } + + public Iterator getBodyFrameIterator(AMQProtocolSession protocolSession, int channel) + { + return new BodyFrameIterator(protocolSession, channel); + } + + public Iterator getContentBodyIterator() + { + return new BodyContentIterator(); + } + + public ContentHeaderBody getContentHeaderBody() throws AMQException + { + return _messageHandle.getContentHeaderBody(getStoreContext()); + } + + + + public Long getMessageId() + { + return _messageHandle.getMessageId(); + } + + /** + * Creates a long-lived reference to this message, and increments the count of such references, as an atomic + * operation. + */ + public AMQMessage takeReference() + { + incrementReference(); // _referenceCount.incrementAndGet(); + + return this; + } + + public boolean incrementReference() + { + return incrementReference(1); + } + + /* Threadsafe. Increment the reference count on the message. */ + public boolean incrementReference(int count) + { + if(_referenceCount.addAndGet(count) <= 1) + { + _referenceCount.addAndGet(-count); + return false; + } + else + { + return true; + } + + } + + /** + * Threadsafe. This will decrement the reference count and when it reaches zero will remove the message from the + * message store. + * + * @param storeContext + * + * @throws MessageCleanupException when an attempt was made to remove the message from the message store and that + * failed + */ + public void decrementReference(StoreContext storeContext) throws MessageCleanupException + { + + int count = _referenceCount.decrementAndGet(); + + // note that the operation of decrementing the reference count and then removing the message does not + // have to be atomic since the ref count starts at 1 and the exchange itself decrements that after + // the message has been passed to all queues. i.e. we are + // not relying on the all the increments having taken place before the delivery manager decrements. + if (count == 0) + { + // set the reference count way below 0 so that we can detect that the message has been deleted + // this is to guard against the message being spontaneously recreated (from the mgmt console) + // by copying from other queues at the same time as it is being removed. + _referenceCount.set(Integer.MIN_VALUE/2); + + try + { + // must check if the handle is null since there may be cases where we decide to throw away a message + // and the handle has not yet been constructed + if (_messageHandle != null) + { + _messageHandle.removeMessage(storeContext); + } + } + catch (AMQException e) + { + // to maintain consistency, we revert the count + incrementReference(); + throw new MessageCleanupException(getMessageId(), e); + } + } + else + { + if (count < 0) + { + throw new MessageCleanupException("Reference count for message id " + debugIdentity() + + " has gone below 0."); + } + } + } + + + /** + * Called selectors to determin if the message has already been sent + * + * @return _deliveredToConsumer + */ + public boolean getDeliveredToConsumer() + { + return (_flags & DELIVERED_TO_CONSUMER) != 0; + } + + public boolean isPersistent() throws AMQException + { + return _messageHandle.isPersistent(); + } + + /** + * Called to enforce the 'immediate' flag. + * + * @returns true if the message is marked for immediate delivery but has not been marked as delivered + * to a consumer + */ + public boolean immediateAndNotDelivered() + { + + return (_flags & IMMEDIATE_AND_DELIVERED) == IMMEDIATE; + + } + + public MessagePublishInfo getMessagePublishInfo() throws AMQException + { + return _messageHandle.getMessagePublishInfo(getStoreContext()); + } + + public boolean isRedelivered() + { + return _messageHandle.isRedelivered(); + } + + public void setRedelivered(boolean redelivered) + { + _messageHandle.setRedelivered(redelivered); + } + + public long getArrivalTime() + { + return _messageHandle.getArrivalTime(); + } + + /** + * Checks to see if the message has expired. If it has the message is dequeued. + * + * @param queue The queue to check the expiration against. (Currently not used) + * + * @return true if the message has expire + * + * @throws AMQException + */ + public boolean expired(AMQQueue queue) throws AMQException + { + + if (_expiration != 0L) + { + long now = System.currentTimeMillis(); + + return (now > _expiration); + } + + return false; + } + + /** + * Called when this message is delivered to a consumer. (used to implement the 'immediate' flag functionality). + * And for selector efficiency. + */ + public void setDeliveredToConsumer() + { + _flags |= DELIVERED_TO_CONSUMER; + } + + + + public AMQMessageHandle getMessageHandle() + { + return _messageHandle; + } + + public long getSize() + { + return _size; + + } + + public Object getPublisherClientInstance() + { + return _sessionIdentifier.getSessionInstance(); + } + + public Object getPublisherIdentifier() + { + return _sessionIdentifier.getSessionIdentifier(); + } + + public void setClientIdentifier(final AMQProtocolSession.ProtocolSessionIdentifier sessionIdentifier) + { + _sessionIdentifier = sessionIdentifier; + } + + + public String toString() + { + // return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken : " + + // _taken + " by :" + _takenBySubcription; + + return "Message[" + debugIdentity() + "]: " + getMessageId() + "; ref count: " + _referenceCount; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java new file mode 100644 index 0000000000..0ddd4e4d92 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +/** + * A pluggable way of getting message data. Implementations can provide intelligent caching for example or + * even no caching at all to minimise the broker memory footprint. + */ +public interface AMQMessageHandle +{ + ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException; + + /** + * + * @return the messageId for the message associated with this handle + */ + Long getMessageId(); + + + /** + * @return the number of body frames associated with this message + */ + int getBodyCount(StoreContext context) throws AMQException; + + /** + * @return the size of the body + */ + long getBodySize(StoreContext context) throws AMQException; + + /** + * Get a particular content body + * @param index the index of the body to retrieve, must be between 0 and getBodyCount() - 1 + * @return a content body + * @throws IllegalArgumentException if the index is invalid + */ + ContentChunk getContentChunk(StoreContext context, int index) throws IllegalArgumentException, AMQException; + + void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody) throws AMQException; + + MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException; + + boolean isRedelivered(); + + void setRedelivered(boolean redelivered); + + boolean isPersistent(); + + void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo, + ContentHeaderBody contentHeaderBody) + throws AMQException; + + void removeMessage(StoreContext storeContext) throws AMQException; + + long getArrivalTime(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java new file mode 100644 index 0000000000..34a70c6969 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -0,0 +1,71 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.AMQException; + +public class AMQPriorityQueue extends SimpleAMQQueue +{ + protected AMQPriorityQueue(final AMQShortString name, + final boolean durable, + final AMQShortString owner, + final boolean autoDelete, + final VirtualHost virtualHost, + int priorities) + throws AMQException + { + super(name, durable, owner, autoDelete, virtualHost, new PriorityQueueList.Factory(priorities)); + } + + public int getPriorities() + { + return ((PriorityQueueList) _entries).getPriorities(); + } + + @Override + protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + { + // check that all subscriptions are not in advance of the entry + SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator(); + while(subIter.advance() && !entry.isAcquired()) + { + final Subscription subscription = subIter.getNode().getSubscription(); + QueueEntry subnode = subscription.getLastSeenEntry(); + while(subnode != null && entry.compareTo(subnode) < 0 && !entry.isAcquired()) + { + if(subscription.setLastSeenEntry(subnode,entry)) + { + break; + } + else + { + subnode = subscription.getLastSeenEntry(); + } + } + + } + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java new file mode 100644 index 0000000000..03ccbe7ce4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -0,0 +1,216 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration.CompositeConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +import java.util.List; +import java.util.Set; + +public interface AMQQueue extends Managable, Comparable +{ + + AMQShortString getName(); + + boolean isDurable(); + + boolean isAutoDelete(); + + AMQShortString getOwner(); + + VirtualHost getVirtualHost(); + + + void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException; + + void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException; + + List getExchangeBindings(); + + + void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException; + + void unregisterSubscription(final Subscription subscription) throws AMQException; + + + int getConsumerCount(); + + int getActiveConsumerCount(); + + boolean isUnused(); + + boolean isEmpty(); + + int getMessageCount(); + + int getUndeliveredMessageCount(); + + + long getQueueDepth(); + + long getReceivedMessageCount(); + + long getOldestMessageArrivalTime(); + + boolean isDeleted(); + + + int delete() throws AMQException; + + + QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException; + + void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException; + + void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException; + + + + boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; + + + + void addQueueDeleteTask(final Task task); + + + List getMessagesOnTheQueue(); + + List getMessagesOnTheQueue(long fromMessageId, long toMessageId); + + List getMessagesOnTheQueue(int num); + + List getMessagesOnTheQueue(int num, int offest); + + QueueEntry getMessageOnTheQueue(long messageId); + + + void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, + StoreContext storeContext); + + void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext); + + void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext); + + + + long getMaximumMessageSize(); + + void setMaximumMessageSize(long value); + + + long getMaximumMessageCount(); + + void setMaximumMessageCount(long value); + + + long getMaximumQueueDepth(); + + void setMaximumQueueDepth(long value); + + + long getMaximumMessageAge(); + + void setMaximumMessageAge(final long maximumMessageAge); + + + long getMinimumAlertRepeatGap(); + + + void deleteMessageFromTop(StoreContext storeContext) throws AMQException; + + long clearQueue(StoreContext storeContext) throws AMQException; + + + + void removeExpiredIfNoSubscribers() throws AMQException; + + Set getNotificationChecks(); + + void flushSubscription(final Subscription sub) throws AMQException; + + void deliverAsync(final Subscription sub); + + void deliverAsync(); + + void stop(); + + + /** + * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription + * already exists. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to create a subscription, because an exclusive subscription already exists. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingExclusiveSubscription extends AMQException + { + + public ExistingExclusiveSubscription() + { + super(""); + } + } + + /** + * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription + * already exists. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to create an exclusize subscription, as a subscription already exists. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingSubscriptionPreventsExclusive extends AMQException + { + public ExistingSubscriptionPreventsExclusive() + { + super(""); + } + } + + static interface Task + { + public void doTask(AMQQueue queue) throws AMQException; + } + + void configure(Configuration virtualHostDefaultQueueConfiguration); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java new file mode 100644 index 0000000000..be8c19d18f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -0,0 +1,78 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration.Configuration; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.AMQException; + + +public class AMQQueueFactory +{ + public static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities"); + + public static AMQQueue createAMQQueueImpl(AMQShortString name, + boolean durable, + AMQShortString owner, + boolean autoDelete, + VirtualHost virtualHost, final FieldTable arguments) + + throws AMQException + { + + return createAMQQueueImpl(name, durable, owner, autoDelete, + virtualHost, arguments, + VirtualHostConfiguration.getDefaultQueueConfiguration(virtualHost)); + } + + public static AMQQueue createAMQQueueImpl(AMQShortString name, + boolean durable, + AMQShortString owner, + boolean autoDelete, + VirtualHost virtualHost, final FieldTable arguments, + Configuration queueConfiguration) + throws AMQException + { + + final int priorities = arguments == null ? 1 : arguments.containsKey(X_QPID_PRIORITIES) ? arguments.getInteger(X_QPID_PRIORITIES) : 1; + + AMQQueue q = null; + if(priorities > 1) + { + q = new AMQPriorityQueue(name, durable, owner, autoDelete, virtualHost, priorities); + } + else + { + q = new SimpleAMQQueue(name, durable, owner, autoDelete, virtualHost); + } + if (q != null && queueConfiguration != null) + { + q.configure(queueConfiguration); + } + + //Register the new queue + virtualHost.getQueueRegistry().registerQueue(q); + return q; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java new file mode 100644 index 0000000000..2ed6be77c6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -0,0 +1,479 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.CommonContentHeaderProperties; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.store.StoreContext; + +import javax.management.JMException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +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.text.SimpleDateFormat; +import java.util.*; + +/** + * 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); + + private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("MM-dd-yy HH:mm:ss.SSS z"); + + /** + * Since the MBean is not associated with a real channel we can safely create our own store context + * for use in the few methods that require one. + */ + private StoreContext _storeContext = new StoreContext(); + + private AMQQueue _queue = null; + private String _queueName = null; + // OpenMBean data types for viewMessages method + private static final String[] _msgAttributeNames = { "AMQ MessageId", "Header", "Size(bytes)", "Redelivered" }; + private static String[] _msgAttributeIndex = { _msgAttributeNames[0] }; + private static OpenType[] _msgAttributeTypes = new OpenType[4]; // 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 final String[] _msgContentAttributes = { "AMQ MessageId", "MimeType", "Encoding", "Content" }; + 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 = jmxEncode(new StringBuffer(queue.getName()), 0).toString(); + } + + 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", _msgContentAttributes, _msgContentAttributes, + _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 + + _messageDataType = + new CompositeType("Message", "AMQ Message", _msgAttributeNames, _msgAttributeNames, _msgAttributeTypes); + _messagelistDataType = new TabularType("Messages", "List of messages", _messageDataType, _msgAttributeIndex); + } + + public String getObjectInstanceName() + { + return _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 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); + } + + public Long getMaximumQueueDepth() + { + long queueDepthInBytes = _queue.getMaximumQueueDepth(); + + return queueDepthInBytes >> 10; + } + + public void setMaximumQueueDepth(Long value) + { + _queue.setMaximumQueueDepth(value); + } + + /** + * returns the size of messages(KB) in the queue. + */ + public Long getQueueDepth() throws JMException + { + long queueBytesSize = _queue.getQueueDepth(); + + return queueBytesSize >> 10; + } + + /** + * Checks if there is any notification to be send to the listeners + */ + public void checkForNotification(AMQMessage msg) throws AMQException, JMException + { + + 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.getName() + " - " + notificationMsg); + notificationMsg = notification.name() + " " + notificationMsg; + + _lastNotification = + new Notification(MonitorNotification.THRESHOLD_VALUE_EXCEEDED, this, ++_notificationSequenceNumber, + System.currentTimeMillis(), notificationMsg); + + _broadcaster.sendNotification(_lastNotification); + } + + public Notification getLastNotification() + { + return _lastNotification; + } + + /** + * @see AMQQueue#deleteMessageFromTop + */ + public void deleteMessageFromTop() throws JMException + { + try + { + _queue.deleteMessageFromTop(_storeContext); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * @see AMQQueue#clearQueue + */ + public void clearQueue() throws JMException + { + try + { + _queue.clearQueue(_storeContext); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * 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); + } + + AMQMessage msg = entry.getMessage(); + // get message content + Iterator cBodies = msg.getContentBodyIterator(); + List msgContent = new ArrayList(); + while (cBodies.hasNext()) + { + ContentChunk body = cBodies.next(); + if (body.getSize() != 0) + { + if (body.getSize() != 0) + { + ByteBuffer slice = body.getData().slice(); + for (int j = 0; j < slice.limit(); j++) + { + msgContent.add(slice.get()); + } + } + } + } + + try + { + // Create header attributes list + CommonContentHeaderProperties headerProperties = + (CommonContentHeaderProperties) msg.getContentHeaderBody().properties; + String mimeType = null, encoding = null; + if (headerProperties != null) + { + AMQShortString mimeTypeShortSting = headerProperties.getContentType(); + mimeType = (mimeTypeShortSting == null) ? null : mimeTypeShortSting.toString(); + encoding = (headerProperties.getEncoding() == null) ? "" : headerProperties.getEncoding().toString(); + } + + Object[] itemValues = { msgId, mimeType, encoding, msgContent.toArray(new Byte[0]) }; + + return new CompositeDataSupport(_msgContentType, _msgContentAttributes, itemValues); + } + catch (AMQException e) + { + JMException jme = new JMException("Error creating header attributes list: " + e); + jme.initCause(e); + throw jme; + } + } + + /** + * Returns the header contents of the messages stored in this queue in tabular form. + */ + public TabularData viewMessages(int beginIndex, int endIndex) throws JMException + { + if ((beginIndex > endIndex) || (beginIndex < 1)) + { + throw new OperationsException("From Index = " + beginIndex + ", To Index = " + endIndex + + "\n\"From Index\" should be greater than 0 and less than \"To Index\""); + } + + List list = _queue.getMessagesOnTheQueue(); + TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType); + + try + { + // Create the tabular list of message header contents + for (int i = beginIndex; (i <= endIndex) && (i <= list.size()); i++) + { + AMQMessage msg = list.get(i - 1).getMessage(); + ContentHeaderBody headerBody = msg.getContentHeaderBody(); + // Create header attributes list + String[] headerAttributes = getMessageHeaderProperties(headerBody); + Object[] itemValues = { msg.getMessageId(), headerAttributes, headerBody.bodySize, msg.isRedelivered() }; + CompositeData messageData = new CompositeDataSupport(_messageDataType, _msgAttributeNames, 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.properties; + 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 == 1) ? "Persistent" : "Non_Persistent")); + + list.add("JMSPriority = " + headerProperties.getPriority()); + list.add("JMSType = " + headerProperties.getType()); + + long longDate = headerProperties.getExpiration(); + String strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; + list.add("JMSExpiration = " + strDate); + + longDate = headerProperties.getTimestamp(); + strDate = (longDate != 0) ? _dateFormat.format(new Date(longDate)) : null; + list.add("JMSTimestamp = " + strDate); + + return list.toArray(new String[list.size()]); + } + + /** + * @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 then 0 and less then \"To MessageId\""); + } + + _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, _storeContext); + } + + /** + * 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/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java new file mode 100644 index 0000000000..290fedcf7b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.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.queue; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.apache.qpid.configuration.Configured; +import org.apache.qpid.server.registry.ApplicationRegistry; + +public class AsyncDeliveryConfig +{ + private Executor _executor; + + @Configured(path = "delivery.poolsize", defaultValue = "0") + public int poolSize; + + public Executor getExecutor() + { + if (_executor == null) + { + if (poolSize > 0) + { + _executor = Executors.newFixedThreadPool(poolSize); + } + else + { + _executor = Executors.newCachedThreadPool(); + } + } + return _executor; + } + + public static Executor getAsyncDeliveryExecutor() + { + return ApplicationRegistry.getInstance().getConfiguredObject(AsyncDeliveryConfig.class).getExecutor(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java new file mode 100644 index 0000000000..cbe9246f09 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class DefaultQueueRegistry implements QueueRegistry +{ + private ConcurrentMap _queueMap = new ConcurrentHashMap(); + + private final VirtualHost _virtualHost; + + public DefaultQueueRegistry(VirtualHost virtualHost) + { + _virtualHost = virtualHost; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public void registerQueue(AMQQueue queue) throws AMQException + { + _queueMap.put(queue.getName(), queue); + } + + public void unregisterQueue(AMQShortString name) throws AMQException + { + _queueMap.remove(name); + } + + public AMQQueue getQueue(AMQShortString name) + { + return _queueMap.get(name); + } + + public Collection getQueueNames() + { + return _queueMap.keySet(); + } + + public Collection getQueues() + { + return _queueMap.values(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java new file mode 100644 index 0000000000..a2fcab9e73 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.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.queue; + +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +public class ExchangeBinding +{ + private final Exchange _exchange; + private final AMQShortString _routingKey; + private final FieldTable _arguments; + + private static final FieldTable EMPTY_ARGUMENTS = new FieldTable(); + + ExchangeBinding(AMQShortString routingKey, Exchange exchange) + { + this(routingKey, exchange, EMPTY_ARGUMENTS); + } + + ExchangeBinding(AMQShortString routingKey, Exchange exchange, FieldTable arguments) + { + _routingKey = routingKey == null ? AMQShortString.EMPTY_STRING : routingKey; + _exchange = exchange; + _arguments = arguments == null ? EMPTY_ARGUMENTS : arguments; + } + + void unbind(AMQQueue queue) throws AMQException + { + _exchange.deregisterQueue(_routingKey, queue, _arguments); + } + + public Exchange getExchange() + { + return _exchange; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + + public FieldTable getArguments() + { + return _arguments; + } + + public int hashCode() + { + return (_exchange == null ? 0 : _exchange.hashCode()) + + (_routingKey == null ? 0 : _routingKey.hashCode()); + } + + public boolean equals(Object o) + { + if (!(o instanceof ExchangeBinding)) + { + return false; + } + ExchangeBinding eb = (ExchangeBinding) o; + return _exchange.equals(eb._exchange) + && _routingKey.equals(eb._routingKey); + } +} \ No newline at end of file diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java new file mode 100644 index 0000000000..fb839c1783 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.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.queue; + +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.exchange.Exchange; + +/** + * When a queue is deleted, it should be deregistered from any + * exchange it has been bound to. This class assists in this task, + * by keeping track of all bindings for a given queue. + */ +class ExchangeBindings +{ + private final List _bindings = new CopyOnWriteArrayList(); + private final AMQQueue _queue; + + ExchangeBindings(AMQQueue queue) + { + _queue = queue; + } + + /** + * Adds the specified binding to those being tracked. + * @param routingKey the routing key with which the queue whose bindings + * are being tracked by the instance has been bound to the exchange + * @param exchange the exchange bound to + */ + void addBinding(AMQShortString routingKey, FieldTable arguments, Exchange exchange) + { + _bindings.add(new ExchangeBinding(routingKey, exchange, arguments)); + } + + + public boolean remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange) + { + return _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments)); + } + + + /** + * Deregisters this queue from any exchange it has been bound to + */ + void deregister() throws AMQException + { + //remove duplicates at this point + HashSet copy = new HashSet(_bindings); + for (ExchangeBinding b : copy) + { + b.unbind(_queue); + } + } + + List getExchangeBindings() + { + return _bindings; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java new file mode 100644 index 0000000000..6466e81dd2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; + +/** + * Signals that the dequeue of a message from a queue failed. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Indicates the a message could not be dequeued from a queue. + *
+ *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Happens as a consequence of a message store failure, or reference counting error. Both of which migh become + * runtime exceptions, as unrecoverable conditions? In which case this one might be dropped too. + */ +public class FailedDequeueException extends AMQException +{ + public FailedDequeueException(String queue) + { + super("Failed to dequeue message from " + queue); + } + + public FailedDequeueException(String queue, AMQException e) + { + super("Failed to dequeue message from " + queue, e); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java new file mode 100644 index 0000000000..d38932bb61 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.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.queue; + +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.AMQException; + +public interface Filterable +{ + ContentHeaderBody getContentHeaderBody() throws E; + + boolean isPersistent() throws E; + + boolean isRedelivered(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java new file mode 100644 index 0000000000..35ad5be4e0 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.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.queue; + +import java.util.LinkedList; +import java.util.List; +import java.util.Collections; +import java.util.ArrayList; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.store.StoreContext; + +/** + */ +public class InMemoryMessageHandle implements AMQMessageHandle +{ + + private ContentHeaderBody _contentHeaderBody; + + private MessagePublishInfo _messagePublishInfo; + + private List _contentBodies; + + private boolean _redelivered; + + private long _arrivalTime; + + private final Long _messageId; + + public InMemoryMessageHandle(final Long messageId) + { + _messageId = messageId; + } + + public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException + { + return _contentHeaderBody; + } + + public Long getMessageId() + { + return _messageId; + } + + public int getBodyCount(StoreContext context) + { + return _contentBodies.size(); + } + + public long getBodySize(StoreContext context) throws AMQException + { + return getContentHeaderBody(context).bodySize; + } + + public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException + { + if (index > _contentBodies.size() - 1) + { + throw new IllegalArgumentException("Index " + index + " out of valid range 0 to " + + (_contentBodies.size() - 1)); + } + return _contentBodies.get(index); + } + + public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody) + throws AMQException + { + if(_contentBodies == null) + { + if(isLastContentBody) + { + _contentBodies = Collections.singletonList(contentBody); + } + else + { + _contentBodies = new ArrayList(); + _contentBodies.add(contentBody); + } + } + else + { + _contentBodies.add(contentBody); + } + } + + public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException + { + return _messagePublishInfo; + } + + public boolean isRedelivered() + { + return _redelivered; + } + + + public void setRedelivered(boolean redelivered) + { + _redelivered = redelivered; + } + + public boolean isPersistent() + { + return false; + } + + /** + * This is called when all the content has been received. + * @param messagePublishInfo + * @param contentHeaderBody + * @throws AMQException + */ + public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo, + ContentHeaderBody contentHeaderBody) + throws AMQException + { + _messagePublishInfo = messagePublishInfo; + _contentHeaderBody = contentHeaderBody; + if(contentHeaderBody.bodySize == 0) + { + _contentBodies = Collections.EMPTY_LIST; + } + _arrivalTime = System.currentTimeMillis(); + } + + public void removeMessage(StoreContext storeContext) throws AMQException + { + // NO OP + } + + public long getArrivalTime() + { + return _arrivalTime; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java new file mode 100644 index 0000000000..b994040131 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -0,0 +1,319 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.exchange.NoRouteException; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; + +public class IncomingMessage implements Filterable +{ + + /** Used for debugging purposes. */ + private static final Logger _logger = Logger.getLogger(IncomingMessage.class); + + private static final boolean SYNCHED_CLOCKS = + ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false); + + private final MessagePublishInfo _messagePublishInfo; + private ContentHeaderBody _contentHeaderBody; + private AMQMessageHandle _messageHandle; + private final Long _messageId; + private final TransactionalContext _txnContext; + + private static final boolean MSG_AUTH = + ApplicationRegistry.getInstance().getConfiguration().getBoolean("security.msg-auth", false); + + + /** + * Keeps a track of how many bytes we have received in body frames + */ + private long _bodyLengthReceived = 0; + + /** + * This is stored during routing, to know the queues to which this message should immediately be + * delivered. It is cleared after delivery has been attempted. Any persistent record of destinations is done + * by the message handle. + */ + private ArrayList _destinationQueues; + + private AMQProtocolSession _publisher; + private MessageStore _messageStore; + private long _expiration; + + private Exchange _exchange; + + + public IncomingMessage(final Long messageId, + final MessagePublishInfo info, + final TransactionalContext txnContext, + final AMQProtocolSession publisher) + { + _messageId = messageId; + _messagePublishInfo = info; + _txnContext = txnContext; + _publisher = publisher; + + } + + public void setContentHeaderBody(final ContentHeaderBody contentHeaderBody) throws AMQException + { + _contentHeaderBody = contentHeaderBody; + } + + public void setExpiration() + { + long expiration = + ((BasicContentHeaderProperties) _contentHeaderBody.properties).getExpiration(); + long timestamp = + ((BasicContentHeaderProperties) _contentHeaderBody.properties).getTimestamp(); + + if (SYNCHED_CLOCKS) + { + _expiration = expiration; + } + else + { + // Update TTL to be in broker time. + if (expiration != 0L) + { + if (timestamp != 0L) + { + // todo perhaps use arrival time + long diff = (System.currentTimeMillis() - timestamp); + + if ((diff > 1000L) || (diff < 1000L)) + { + _expiration = expiration + diff; + } + } + } + } + + } + + public void routingComplete(final MessageStore store, + final MessageHandleFactory factory) throws AMQException + { + + final boolean persistent = isPersistent(); + _messageHandle = factory.createMessageHandle(_messageId, store, persistent); + if (persistent) + { + _txnContext.beginTranIfNecessary(); + // enqueuing the messages ensure that if required the destinations are recorded to a + // persistent store + + if(_destinationQueues != null) + { + for (int i = 0; i < _destinationQueues.size(); i++) + { + store.enqueueMessage(_txnContext.getStoreContext(), + _destinationQueues.get(i), _messageId); + } + } + } + } + + public AMQMessage deliverToQueues() + throws AMQException + { + + // we get a reference to the destination queues now so that we can clear the + // transient message data as quickly as possible + if (_logger.isDebugEnabled()) + { + _logger.debug("Delivering message " + _messageId + " to " + _destinationQueues); + } + + AMQMessage message = null; + + try + { + // first we allow the handle to know that the message has been fully received. This is useful if it is + // maintaining any calculated values based on content chunks + _messageHandle.setPublishAndContentHeaderBody(_txnContext.getStoreContext(), + _messagePublishInfo, getContentHeaderBody()); + + + + message = new AMQMessage(_messageHandle,_txnContext.getStoreContext(), _messagePublishInfo); + + message.setExpiration(_expiration); + message.setClientIdentifier(_publisher.getSessionIdentifier()); + + // we then allow the transactional context to do something with the message content + // now that it has all been received, before we attempt delivery + _txnContext.messageFullyReceived(isPersistent()); + + AMQShortString userID = getContentHeaderBody().properties instanceof BasicContentHeaderProperties ? + ((BasicContentHeaderProperties) getContentHeaderBody().properties).getUserId() : null; + + if (MSG_AUTH && !_publisher.getAuthorizedID().getName().equals(userID == null? "" : userID.toString())) + { + throw new UnauthorizedAccessException("Acccess Refused",message); + } + + if ((_destinationQueues == null) || _destinationQueues.size() == 0) + { + + if (isMandatory() || isImmediate()) + { + throw new NoRouteException("No Route for message", message); + + } + else + { + _logger.warn("MESSAGE DISCARDED: No routes for message - " + message); + } + } + else + { + int offset; + final int queueCount = _destinationQueues.size(); + message.incrementReference(queueCount); + if(queueCount == 1) + { + offset = 0; + } + else + { + offset = ((int)(message.getMessageId().longValue())) % queueCount; + if(offset < 0) + { + offset = -offset; + } + } + for (int i = offset; i < queueCount; i++) + { + // normal deliver so add this message at the end. + _txnContext.deliver(_destinationQueues.get(i), message); + } + for (int i = 0; i < offset; i++) + { + // normal deliver so add this message at the end. + _txnContext.deliver(_destinationQueues.get(i), message); + } + } + + message.clearStoreContext(); + return message; + } + finally + { + // Remove refence for routing process . Reference count should now == delivered queue count + if(message != null) message.decrementReference(_txnContext.getStoreContext()); + } + + } + + public void addContentBodyFrame(final ContentChunk contentChunk) + throws AMQException + { + + _bodyLengthReceived += contentChunk.getSize(); + + _messageHandle.addContentBodyFrame(_txnContext.getStoreContext(), contentChunk, allContentReceived()); + + } + + public boolean allContentReceived() + { + return (_bodyLengthReceived == getContentHeaderBody().bodySize); + } + + public AMQShortString getExchange() throws AMQException + { + return _messagePublishInfo.getExchange(); + } + + public AMQShortString getRoutingKey() throws AMQException + { + return _messagePublishInfo.getRoutingKey(); + } + + public boolean isMandatory() throws AMQException + { + return _messagePublishInfo.isMandatory(); + } + + + public boolean isImmediate() throws AMQException + { + return _messagePublishInfo.isImmediate(); + } + + public ContentHeaderBody getContentHeaderBody() + { + return _contentHeaderBody; + } + + + public boolean isPersistent() + { + //todo remove literal values to a constant file such as AMQConstants in common + return getContentHeaderBody().properties instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) getContentHeaderBody().properties).getDeliveryMode() == 2; + } + + public boolean isRedelivered() + { + return false; + } + + public void setMessageStore(final MessageStore messageStore) + { + _messageStore = messageStore; + } + + public Long getMessageId() + { + return _messageId; + } + + public void setExchange(final Exchange e) + { + _exchange = e; + } + + public void route() throws AMQException + { + _exchange.route(this); + } + + public void enqueue(final ArrayList queues) + { + _destinationQueues = queues; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java new file mode 100644 index 0000000000..2bc94995e9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java @@ -0,0 +1,245 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import java.io.IOException; + +import javax.management.JMException; +import javax.management.MBeanOperationInfo; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.management.MBeanAttribute; +import org.apache.qpid.server.management.MBeanOperation; +import org.apache.qpid.server.management.MBeanOperationParameter; + +/** + * The management interface exposed to allow management of a queue. + * @author Robert J. Greig + * @author Bhupendra Bhardwaj + * @version 0.1 + */ +public interface ManagedQueue +{ + static final String TYPE = "Queue"; + + /** + * Returns the Name of the ManagedQueue. + * @return the name of the managedQueue. + * @throws IOException + */ + @MBeanAttribute(name="Name", description = TYPE + " Name") + String getName() throws IOException; + + /** + * Total number of messages on the queue, which are yet to be delivered to the consumer(s). + * @return number of undelivered message in the Queue. + * @throws IOException + */ + @MBeanAttribute(name="MessageCount", description = "Total number of undelivered messages on the queue") + Integer getMessageCount() throws IOException; + + /** + * Tells the total number of messages receieved by the queue since startup. + * @return total number of messages received. + * @throws IOException + */ + @MBeanAttribute(name="ReceivedMessageCount", description="The total number of messages receieved by the queue since startup") + Long getReceivedMessageCount() throws IOException; + + /** + * Size of messages in the queue + * @return + * @throws IOException + */ + @MBeanAttribute(name="QueueDepth", description="Size of messages(KB) in the queue") + Long getQueueDepth() throws IOException, JMException; + + /** + * Returns the total number of active subscribers to the queue. + * @return the number of active subscribers + * @throws IOException + */ + @MBeanAttribute(name="ActiveConsumerCount", description="The total number of active subscribers to the queue") + Integer getActiveConsumerCount() throws IOException; + + /** + * Returns the total number of subscribers to the queue. + * @return the number of subscribers. + * @throws IOException + */ + @MBeanAttribute(name="ConsumerCount", description="The total number of subscribers to the queue") + Integer getConsumerCount() throws IOException; + + /** + * Tells the Owner of the ManagedQueue. + * @return the owner's name. + * @throws IOException + */ + @MBeanAttribute(name="Owner", description = "Owner") + String getOwner() throws IOException; + + /** + * Tells whether this ManagedQueue is durable or not. + * @return true if this ManagedQueue is a durable queue. + * @throws IOException + */ + @MBeanAttribute(name="Durable", description = "true if the AMQQueue is durable") + boolean isDurable() throws IOException; + + /** + * Tells if the ManagedQueue is set to AutoDelete. + * @return true if the ManagedQueue is set to AutoDelete. + * @throws IOException + */ + @MBeanAttribute(name="AutoDelete", description = "true if the AMQQueue is AutoDelete") + boolean isAutoDelete() throws IOException; + + /** + * Returns the maximum age of a message (expiration time) + * @return the maximum age + * @throws IOException + */ + Long getMaximumMessageAge() throws IOException; + + /** + * Sets the maximum age of a message + * @param age maximum age of message. + * @throws IOException + */ + @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on the broker") + void setMaximumMessageAge(Long age) throws IOException; + + /** + * Returns the maximum size of a message (in kbytes) allowed to be accepted by the + * ManagedQueue. This is useful in setting notifications or taking + * appropriate action, if the size of the message received is more than + * the allowed size. + * @return the maximum size of a message allowed to be aceepted by the + * ManagedQueue. + * @throws IOException + */ + Long getMaximumMessageSize() throws IOException; + + /** + * Sets the maximum size of the message (in kbytes) that is allowed to be + * accepted by the Queue. + * @param size maximum size of message. + * @throws IOException + */ + @MBeanAttribute(name="MaximumMessageSize", description="Threshold high value(KB) for a message size") + void setMaximumMessageSize(Long size) throws IOException; + + /** + * Tells the maximum number of messages that can be stored in the queue. + * This is useful in setting the notifications or taking required + * action is the number of message increase this limit. + * @return maximum muber of message allowed to be stored in the queue. + * @throws IOException + */ + Long getMaximumMessageCount() throws IOException; + + /** + * Sets the maximum number of messages allowed to be stored in the queue. + * @param value the maximum number of messages allowed to be stored in the queue. + * @throws IOException + */ + @MBeanAttribute(name="MaximumMessageCount", description="Threshold high value for number of undelivered messages in the queue") + void setMaximumMessageCount(Long value) throws IOException; + + /** + * This is useful for setting notifications or taking required action if the size of messages + * stored in the queue increases over this limit. + * @return threshold high value for Queue Depth + * @throws IOException + */ + Long getMaximumQueueDepth() throws IOException; + + /** + * Sets the maximum size of all the messages together, that can be stored + * in the queue. + * @param value + * @throws IOException + */ + @MBeanAttribute(name="MaximumQueueDepth", description="The threshold high value(KB) for Queue Depth") + void setMaximumQueueDepth(Long value) throws IOException; + + + + //********** Operations *****************// + + + /** + * Returns a subset of all the messages stored in the queue. The messages + * are returned based on the given index numbers. + * @param fromIndex + * @param toIndex + * @return + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="viewMessages", + description="Message headers for messages in this queue within given index range. eg. from index 1 - 100") + TabularData viewMessages(@MBeanOperationParameter(name="from index", description="from index")int fromIndex, + @MBeanOperationParameter(name="to index", description="to index")int toIndex) + throws IOException, JMException, AMQException; + + @MBeanOperation(name="viewMessageContent", description="The message content for given Message Id") + CompositeData viewMessageContent(@MBeanOperationParameter(name="Message Id", description="Message Id")long messageId) + throws IOException, JMException; + + /** + * Deletes the first message from top. + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="deleteMessageFromTop", description="Deletes the first message from top", + impact= MBeanOperationInfo.ACTION) + void deleteMessageFromTop() throws IOException, JMException; + + /** + * Clears the queue by deleting all the undelivered messages from the queue. + * @throws IOException + * @throws JMException + */ + @MBeanOperation(name="clearQueue", + description="Clears the queue by deleting all the undelivered messages from the queue", + impact= MBeanOperationInfo.ACTION) + void clearQueue() throws IOException, JMException; + + /** + * Moves the messages in given range of message Ids to given Queue. QPID-170 + * @param fromMessageId first in the range of message ids + * @param toMessageId last in the range of message ids + * @param toQueue where the messages are to be moved + * @throws IOException + * @throws JMException + * @throws AMQException + */ + @MBeanOperation(name="moveMessages", + description="You can move messages to another queue from this queue ", + impact= MBeanOperationInfo.ACTION) + void moveMessages(@MBeanOperationParameter(name="from MessageId", description="from MessageId")long fromMessageId, + @MBeanOperationParameter(name="to MessageId", description="to MessageId")long toMessageId, + @MBeanOperationParameter(name= ManagedQueue.TYPE, description="to Queue Name")String toQueue) + throws IOException, JMException, AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java new file mode 100644 index 0000000000..090096d3c3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.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.queue; + +import org.apache.qpid.AMQException; + +/** + * MessageCleanupException represents the failure to perform reference counting on messages correctly. This should not + * happen, but there may be programming errors giving race conditions that cause the reference counting to go wrong. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Signals that the reference count of a message has gone below zero. + *
Indicates that a message store has lost a message which is still referenced. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo The race conditions leading to this error should be cleaned up, and a runtime exception used instead. If the + * message store loses messages, then something is seriously wrong and it would be sensible to terminate the + * broker. This may be disguising out of memory errors. + */ +public class MessageCleanupException extends AMQException +{ + public MessageCleanupException(long messageId, AMQException e) + { + super("Failed to cleanup message with id " + messageId, e); + } + + public MessageCleanupException(String message) + { + super(message); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java new file mode 100644 index 0000000000..0b214ca336 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.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.queue; + +import org.apache.qpid.server.store.MessageStore; + +/** + * Constructs a message handle based on the publish body, the content header and the queue to which the message + * has been routed. + * + * @author Robert Greig (robert.j.greig@jpmorgan.com) + */ +public class MessageHandleFactory +{ + + public AMQMessageHandle createMessageHandle(Long messageId, MessageStore store, boolean persistent) + { + // just hardcoded for now + if (persistent) + { + return new WeakReferenceMessageHandle(messageId, store); + } + else + { + return new InMemoryMessageHandle(messageId); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java new file mode 100644 index 0000000000..6118a4c11f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java @@ -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. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +/** + * Encapsulates a publish body and a content header. In the context of the message store these are treated as a + * single unit. + */ +public class MessageMetaData +{ + private MessagePublishInfo _messagePublishInfo; + + private ContentHeaderBody _contentHeaderBody; + + private int _contentChunkCount; + + private long _arrivalTime; + + public MessageMetaData(MessagePublishInfo publishBody, ContentHeaderBody contentHeaderBody, int contentChunkCount) + { + this(publishBody,contentHeaderBody, contentChunkCount, System.currentTimeMillis()); + } + + public MessageMetaData(MessagePublishInfo publishBody, ContentHeaderBody contentHeaderBody, int contentChunkCount, long arrivalTime) + { + _contentHeaderBody = contentHeaderBody; + _messagePublishInfo = publishBody; + _contentChunkCount = contentChunkCount; + _arrivalTime = arrivalTime; + } + + public int getContentChunkCount() + { + return _contentChunkCount; + } + + public void setContentChunkCount(int contentChunkCount) + { + _contentChunkCount = contentChunkCount; + } + + public ContentHeaderBody getContentHeaderBody() + { + return _contentHeaderBody; + } + + public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) + { + _contentHeaderBody = contentHeaderBody; + } + + public MessagePublishInfo getMessagePublishInfo() + { + return _messagePublishInfo; + } + + public void setMessagePublishInfo(MessagePublishInfo messagePublishInfo) + { + _messagePublishInfo = messagePublishInfo; + } + + public long getArrivalTime() + { + return _arrivalTime; + } + + public void setArrivalTime(long arrivalTime) + { + _arrivalTime = arrivalTime; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java new file mode 100644 index 0000000000..d6fd1eec89 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.RequiredDeliveryException; + +/** + * NoConsumersException is a {@link RequiredDeliveryException} that represents the failure case where an immediate + * message cannot be delivered because there are presently no consumers for the message. The AMQP status code, 313, is + * always used to report this condition. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to deliver a message that must be delivered. + *
+ */ +public class NoConsumersException extends RequiredDeliveryException +{ + public NoConsumersException(AMQMessage message) + { + super("Immediate delivery is not possible.", message); + } + + public AMQConstant getReplyCode() + { + return AMQConstant.NO_CONSUMERS; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java new file mode 100644 index 0000000000..6f9efd3200 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java @@ -0,0 +1,138 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; + +public enum NotificationCheck +{ + + MESSAGE_COUNT_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener 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."); + return true; + } + return false; + } + }, + MESSAGE_SIZE_ALERT(true) + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + final long maximumMessageSize = queue.getMaximumMessageSize(); + if(maximumMessageSize != 0) + { + // Check for threshold message size + long messageSize; + try + { + messageSize = (msg == null) ? 0 : msg.getContentHeaderBody().bodySize; + } + catch (AMQException e) + { + messageSize = 0; + } + + + if (messageSize >= maximumMessageSize) + { + listener.notifyClients(this, queue, messageSize + "b : Maximum message size threshold ("+ maximumMessageSize +") breached. [Message ID=" + msg.getMessageId() + "]"); + return true; + } + } + return false; + } + + }, + QUEUE_DEPTH_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + // Check for threshold queue depth in bytes + final long maximumQueueDepth = queue.getMaximumQueueDepth(); + + if(maximumQueueDepth != 0) + { + final long queueDepth = queue.getQueueDepth(); + + if (queueDepth >= maximumQueueDepth) + { + listener.notifyClients(this, queue, (queueDepth>>10) + "Kb : Maximum queue depth threshold ("+(maximumQueueDepth>>10)+"Kb) breached."); + return true; + } + } + return false; + } + + }, + MESSAGE_AGE_ALERT + { + boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener) + { + + final long maxMessageAge = queue.getMaximumMessageAge(); + if(maxMessageAge != 0) + { + final long currentTime = System.currentTimeMillis(); + final long thresholdTime = currentTime - maxMessageAge; + final long firstArrivalTime = queue.getOldestMessageArrivalTime(); + + if(firstArrivalTime < thresholdTime) + { + long oldestAge = currentTime - firstArrivalTime; + listener.notifyClients(this, queue, (oldestAge/1000) + "s : Maximum age on queue threshold ("+(maxMessageAge /1000)+"s) breached."); + + return true; + } + } + return false; + + } + + } + ; + + private final boolean _messageSpecific; + + NotificationCheck() + { + this(false); + } + + NotificationCheck(boolean messageSpecific) + { + _messageSpecific = messageSpecific; + } + + public boolean isMessageSpecific() + { + return _messageSpecific; + } + + abstract boolean notifyIfNecessary(AMQMessage msg, AMQQueue queue, QueueNotificationListener listener); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java new file mode 100644 index 0000000000..fd46a8a5ff --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -0,0 +1,169 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.CommonContentHeaderProperties; +import org.apache.qpid.AMQException; + +public class PriorityQueueList implements QueueEntryList +{ + private final AMQQueue _queue; + private final QueueEntryList[] _priorityLists; + private final int _priorities; + private final int _priorityOffset; + + public PriorityQueueList(AMQQueue queue, int priorities) + { + _queue = queue; + _priorityLists = new QueueEntryList[priorities]; + _priorities = priorities; + _priorityOffset = 5-((priorities + 1)/2); + for(int i = 0; i < priorities; i++) + { + _priorityLists[i] = new SimpleQueueEntryList(queue); + } + } + + public int getPriorities() + { + return _priorities; + } + + public AMQQueue getQueue() + { + return _queue; + } + + public QueueEntry add(AMQMessage message) + { + try + { + int index = ((CommonContentHeaderProperties)((message.getContentHeaderBody().properties))).getPriority() - _priorityOffset; + if(index >= _priorities) + { + index = _priorities-1; + } + else if(index < 0) + { + index = 0; + } + return _priorityLists[index].add(message); + } + catch (AMQException e) + { + // TODO - fix AMQ Exception + throw new RuntimeException(e); + } + + } + + public QueueEntry next(QueueEntry node) + { + QueueEntryImpl nodeImpl = (QueueEntryImpl)node; + QueueEntry next = nodeImpl.getNext(); + + if(next == null) + { + QueueEntryList nodeEntryList = nodeImpl.getQueueEntryList(); + int index; + for(index = _priorityLists.length-1; _priorityLists[index] != nodeEntryList; index--); + + while(next == null && index != 0) + { + index--; + next = ((QueueEntryImpl)_priorityLists[index].getHead()).getNext(); + } + + } + return next; + } + + private final class PriorityQueueEntryListIterator implements QueueEntryIterator + { + private final QueueEntryIterator[] _iterators = new QueueEntryIterator[ _priorityLists.length ]; + private QueueEntry _lastNode; + + PriorityQueueEntryListIterator() + { + for(int i = 0; i < _priorityLists.length; i++) + { + _iterators[i] = _priorityLists[i].iterator(); + } + _lastNode = _iterators[_iterators.length - 1].getNode(); + } + + + public boolean atTail() + { + for(int i = 0; i < _iterators.length; i++) + { + if(!_iterators[i].atTail()) + { + return false; + } + } + return true; + } + + public QueueEntry getNode() + { + return _lastNode; + } + + public boolean advance() + { + for(int i = _iterators.length-1; i >= 0; i--) + { + if(_iterators[i].advance()) + { + _lastNode = _iterators[i].getNode(); + return true; + } + } + return false; + } + } + + public QueueEntryIterator iterator() + { + return new PriorityQueueEntryListIterator(); + } + + public QueueEntry getHead() + { + return _priorityLists[_priorities-1].getHead(); + } + + static class Factory implements QueueEntryListFactory + { + private final int _priorities; + + Factory(int priorities) + { + _priorities = priorities; + } + + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + return new PriorityQueueList(queue, _priorities); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java new file mode 100644 index 0000000000..2657c459a9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -0,0 +1,184 @@ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 interface QueueEntry extends Comparable +{ + + + + public static enum State + { + AVAILABLE, + ACQUIRED, + EXPIRED, + DEQUEUED, + DELETED + } + + public static interface StateChangeListener + { + public void stateChanged(QueueEntry entry, State oldSate, State newState); + } + + public abstract class EntryState + { + private EntryState() + { + } + + public abstract State getState(); + } + + + public final class AvailableState extends EntryState + { + + public State getState() + { + return State.AVAILABLE; + } + } + + + public final class DequeuedState extends EntryState + { + + public State getState() + { + return State.DEQUEUED; + } + } + + + public final class DeletedState extends EntryState + { + + public State getState() + { + return State.DELETED; + } + } + + public final class ExpiredState extends EntryState + { + + public State getState() + { + return State.EXPIRED; + } + } + + + public final class NonSubscriptionAcquiredState extends EntryState + { + public State getState() + { + return State.ACQUIRED; + } + } + + public final class SubscriptionAcquiredState extends EntryState + { + private final Subscription _subscription; + + public SubscriptionAcquiredState(Subscription subscription) + { + _subscription = subscription; + } + + + public State getState() + { + return State.ACQUIRED; + } + + public Subscription getSubscription() + { + return _subscription; + } + } + + + final static EntryState AVAILABLE_STATE = new AvailableState(); + final static EntryState DELETED_STATE = new DeletedState(); + final static EntryState DEQUEUED_STATE = new DequeuedState(); + final static EntryState EXPIRED_STATE = new ExpiredState(); + final static EntryState NON_SUBSCRIPTION_ACQUIRED_STATE = new NonSubscriptionAcquiredState(); + + + + + AMQQueue getQueue(); + + AMQMessage getMessage(); + + long getSize(); + + boolean getDeliveredToConsumer(); + + boolean expired() throws AMQException; + + boolean isAcquired(); + + boolean acquire(); + boolean acquire(Subscription sub); + + boolean delete(); + boolean isDeleted(); + + boolean acquiredBySubscription(); + + void setDeliveredToSubscription(); + + void release(); + + String debugIdentity(); + + boolean immediateAndNotDelivered(); + + void setRedelivered(boolean b); + + Subscription getDeliveredSubscription(); + + void reject(); + + void reject(Subscription subscription); + + boolean isRejectedBy(Subscription subscription); + + void requeue(StoreContext storeContext) throws AMQException; + + void dequeue(final StoreContext storeContext) throws FailedDequeueException; + + void dispose(final StoreContext storeContext) throws MessageCleanupException; + + void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException; + + boolean isQueueDeleted(); + + void addStateChangeListener(StateChangeListener listener); + boolean removeStateChangeListener(StateChangeListener listener); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java new file mode 100644 index 0000000000..dbad5438dc --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -0,0 +1,388 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.log4j.Logger; + +import java.util.Set; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.CopyOnWriteArraySet; + + +public class QueueEntryImpl implements QueueEntry +{ + + /** + * Used for debugging purposes. + */ + private static final Logger _log = Logger.getLogger(QueueEntryImpl.class); + + private final SimpleQueueEntryList _queueEntryList; + + private AMQMessage _message; + + + private Set _rejectedBy = null; + + private volatile EntryState _state = AVAILABLE_STATE; + + private static final + AtomicReferenceFieldUpdater + _stateUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, EntryState.class, "_state"); + + + private volatile Set _stateChangeListeners; + + private static final + AtomicReferenceFieldUpdater + _listenersUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, Set.class, "_stateChangeListeners"); + + + private static final + AtomicLongFieldUpdater + _entryIdUpdater = + AtomicLongFieldUpdater.newUpdater + (QueueEntryImpl.class, "_entryId"); + + + private volatile long _entryId; + + volatile QueueEntryImpl _next; + + + QueueEntryImpl(SimpleQueueEntryList queueEntryList) + { + this(queueEntryList,null,Long.MIN_VALUE); + _state = DELETED_STATE; + } + + + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message, final long entryId) + { + _queueEntryList = queueEntryList; + _message = message; + + _entryIdUpdater.set(this, entryId); + } + + public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message) + { + _queueEntryList = queueEntryList; + _message = message; + } + + protected void setEntryId(long entryId) + { + _entryIdUpdater.set(this, entryId); + } + + protected long getEntryId() + { + return _entryId; + } + + public AMQQueue getQueue() + { + return _queueEntryList.getQueue(); + } + + public AMQMessage getMessage() + { + return _message; + } + + public long getSize() + { + return getMessage().getSize(); + } + + public boolean getDeliveredToConsumer() + { + return getMessage().getDeliveredToConsumer(); + } + + public boolean expired() throws AMQException + { + return getMessage().expired(getQueue()); + } + + public boolean isAcquired() + { + return _state.getState() == State.ACQUIRED; + } + + public boolean acquire() + { + return acquire(NON_SUBSCRIPTION_ACQUIRED_STATE); + } + + private boolean acquire(final EntryState state) + { + boolean acquired = _stateUpdater.compareAndSet(this,AVAILABLE_STATE, state); + if(acquired && _stateChangeListeners != null) + { + notifyStateChange(State.AVAILABLE, State.ACQUIRED); + } + + return acquired; + } + + public boolean acquire(Subscription sub) + { + return acquire(sub.getOwningState()); + } + + public boolean acquiredBySubscription() + { + + return (_state instanceof SubscriptionAcquiredState); + } + + public void setDeliveredToSubscription() + { + getMessage().setDeliveredToConsumer(); + } + + public void release() + { + _stateUpdater.set(this,AVAILABLE_STATE); + } + + public String debugIdentity() + { + return getMessage().debugIdentity(); + } + + + public boolean immediateAndNotDelivered() + { + return _message.immediateAndNotDelivered(); + } + + public void setRedelivered(boolean b) + { + getMessage().setRedelivered(b); + } + + public Subscription getDeliveredSubscription() + { + EntryState state = _state; + if (state instanceof SubscriptionAcquiredState) + { + return ((SubscriptionAcquiredState) state).getSubscription(); + } + else + { + return null; + } + + } + + public void reject() + { + reject(getDeliveredSubscription()); + } + + public void reject(Subscription subscription) + { + if (subscription != null) + { + if (_rejectedBy == null) + { + _rejectedBy = new HashSet(); + } + + _rejectedBy.add(subscription); + } + else + { + _log.warn("Requesting rejection by null subscriber:" + debugIdentity()); + } + } + + public boolean isRejectedBy(Subscription subscription) + { + + if (_rejectedBy != null) // We have subscriptions that rejected this message + { + return _rejectedBy.contains(subscription); + } + else // This messasge hasn't been rejected yet. + { + return false; + } + } + + + public void requeue(final StoreContext storeContext) throws AMQException + { + getQueue().requeue(storeContext, this); + if(_stateChangeListeners != null) + { + notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); + } + } + + public void dequeue(final StoreContext storeContext) throws FailedDequeueException + { + EntryState state = _state; + + if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, DEQUEUED_STATE)) + { + if (state instanceof SubscriptionAcquiredState) + { + Subscription s = ((SubscriptionAcquiredState) state).getSubscription(); + s.restoreCredit(this); + } + + getQueue().dequeue(storeContext, this); + if(_stateChangeListeners != null) + { + notifyStateChange(state.getState() , QueueEntry.State.DEQUEUED); + } + + } + + } + + private void notifyStateChange(final State oldState, final State newState) + { + for(StateChangeListener l : _stateChangeListeners) + { + l.stateChanged(this, oldState, newState); + } + } + + public void dispose(final StoreContext storeContext) throws MessageCleanupException + { + if(delete()) + { + getMessage().decrementReference(storeContext); + } + } + + public void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException + { + //if the queue is null then the message is waiting to be acked, but has been removed. + if (getQueue() != null) + { + dequeue(storeContext); + } + + dispose(storeContext); + } + + public boolean isQueueDeleted() + { + return getQueue().isDeleted(); + } + + public void addStateChangeListener(StateChangeListener listener) + { + Set listeners = _stateChangeListeners; + if(listeners == null) + { + _listenersUpdater.compareAndSet(this, null, new CopyOnWriteArraySet()); + listeners = _stateChangeListeners; + } + + listeners.add(listener); + } + + public boolean removeStateChangeListener(StateChangeListener listener) + { + Set listeners = _stateChangeListeners; + if(listeners != null) + { + return listeners.remove(listener); + } + + return false; + } + + + public int compareTo(final QueueEntry o) + { + QueueEntryImpl other = (QueueEntryImpl)o; + return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0; + } + + public QueueEntryImpl getNext() + { + + QueueEntryImpl next = nextNode(); + while(next != null && next.isDeleted()) + { + + final QueueEntryImpl newNext = next.nextNode(); + if(newNext != null) + { + SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext); + next = nextNode(); + } + else + { + next = null; + } + + } + return next; + } + + QueueEntryImpl nextNode() + { + return _next; + } + + public boolean isDeleted() + { + return _state == DELETED_STATE; + } + + public boolean delete() + { + EntryState state = _state; + + if(state != DELETED_STATE && _stateUpdater.compareAndSet(this,state,DELETED_STATE)) + { + _queueEntryList.advanceHead(); + return true; + } + else + { + return false; + } + } + + public QueueEntryList getQueueEntryList() + { + return _queueEntryList; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java new file mode 100644 index 0000000000..c5c115a2d1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java @@ -0,0 +1,30 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.queue; + +public interface QueueEntryIterator +{ + boolean atTail(); + + QueueEntry getNode(); + + boolean advance(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java new file mode 100644 index 0000000000..313e076f61 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -0,0 +1,34 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 QueueEntryList +{ + AMQQueue getQueue(); + + QueueEntry add(AMQMessage message); + + QueueEntry next(QueueEntry node); + + QueueEntryIterator iterator(); + + QueueEntry getHead(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java new file mode 100644 index 0000000000..4dbce45f67 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java @@ -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. +* +*/ +package org.apache.qpid.server.queue; + +interface QueueEntryListFactory +{ + public QueueEntryList createQueueEntryList(AMQQueue queue); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java new file mode 100644 index 0000000000..959ca03c80 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.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.queue; + + +public interface QueueNotificationListener +{ + void notifyClients(NotificationCheck notification, AMQQueue queue, String notificationMsg); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java new file mode 100644 index 0000000000..1210f0e97c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.server.virtualhost.VirtualHost; + +import java.util.Collection; + +public interface QueueRegistry +{ + VirtualHost getVirtualHost(); + + void registerQueue(AMQQueue queue) throws AMQException; + + void unregisterQueue(AMQShortString name) throws AMQException; + + AMQQueue getQueue(AMQShortString name); + + Collection getQueueNames(); + + Collection getQueues(); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java new file mode 100644 index 0000000000..7e7e8b2114 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -0,0 +1,1604 @@ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.AMQException; +import org.apache.qpid.pool.ReadWriteRunnable; +import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.configuration.Configured; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; + +import javax.management.JMException; +import java.util.List; +import java.util.Set; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 SimpleAMQQueue implements AMQQueue, Subscription.StateListener +{ + private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.class); + + private final AMQShortString _name; + + /** null means shared */ + private final AMQShortString _owner; + + private final boolean _durable; + + /** If true, this queue is deleted when the last subscriber is removed */ + private final boolean _autoDelete; + + private final VirtualHost _virtualHost; + + /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */ + private final ExchangeBindings _bindings = new ExchangeBindings(this); + + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + private final List _deleteTaskList = new CopyOnWriteArrayList(); + + private final AtomicInteger _atomicQueueCount = new AtomicInteger(0); + + private final AtomicLong _atomicQueueSize = new AtomicLong(0L); + + private final AtomicInteger _activeSubscriberCount = new AtomicInteger(); + + protected final SubscriptionList _subscriptionList = new SubscriptionList(this); + private final AtomicReference _lastSubscriptionNode = new AtomicReference(_subscriptionList.getHead()); + + private volatile Subscription _exclusiveSubscriber; + + protected final QueueEntryList _entries; + + private final AMQQueueMBean _managedObject; + private final Executor _asyncDelivery; + private final AtomicLong _totalMessagesReceived = new AtomicLong(); + + /** max allowed size(KB) of a single message */ + @Configured(path = "maximumMessageSize", defaultValue = "0") + public long _maximumMessageSize; + + /** max allowed number of messages on a queue. */ + @Configured(path = "maximumMessageCount", defaultValue = "0") + public long _maximumMessageCount; + + /** max queue depth for the queue */ + @Configured(path = "maximumQueueDepth", defaultValue = "0") + public long _maximumQueueDepth; + + /** maximum message age before alerts occur */ + @Configured(path = "maximumMessageAge", defaultValue = "0") + public long _maximumMessageAge; + + /** the minimum interval between sending out consequetive alerts of the same type */ + @Configured(path = "minimumAlertRepeatGap", defaultValue = "0") + public long _minimumAlertRepeatGap; + + private static final int MAX_ASYNC_DELIVERIES = 10; + + private final Set _notificationChecks = EnumSet.noneOf(NotificationCheck.class); + + private final AtomicLong _stateChangeCount = new AtomicLong(Long.MIN_VALUE); + private AtomicReference _asynchronousRunner = new AtomicReference(null); + private AtomicInteger _deliveredMessages = new AtomicInteger(); + private AtomicBoolean _stopped = new AtomicBoolean(false); + + protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + throws AMQException + { + this(name, durable, owner, autoDelete, virtualHost, new SimpleQueueEntryList.Factory()); + } + + protected SimpleAMQQueue(AMQShortString name, + boolean durable, + AMQShortString owner, + boolean autoDelete, + VirtualHost virtualHost, + QueueEntryListFactory entryListFactory) + throws AMQException + { + + if (name == null) + { + throw new IllegalArgumentException("Queue name must not be null"); + } + + if (virtualHost == null) + { + throw new IllegalArgumentException("Virtual Host must not be null"); + } + + _name = name; + _durable = durable; + _owner = owner; + _autoDelete = autoDelete; + _virtualHost = virtualHost; + _entries = entryListFactory.createQueueEntryList(this); + + _asyncDelivery = ReferenceCountingExecutorService.getInstance().acquireExecutorService(); + + try + { + _managedObject = new AMQQueueMBean(this); + _managedObject.register(); + } + catch (JMException e) + { + throw new AMQException("AMQQueue MBean creation has failed ", e); + } + + resetNotifications(); + + } + + private void resetNotifications() + { + // This ensure that the notification checks for the configured alerts are created. + setMaximumMessageAge(_maximumMessageAge); + setMaximumMessageCount(_maximumMessageCount); + setMaximumMessageSize(_maximumMessageSize); + setMaximumQueueDepth(_maximumQueueDepth); + } + + // ------ Getters and Setters + + public AMQShortString getName() + { + return _name; + } + + public boolean isDurable() + { + return _durable; + } + + public boolean isAutoDelete() + { + return _autoDelete; + } + + public AMQShortString getOwner() + { + return _owner; + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + // ------ bind and unbind + + public void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + exchange.registerQueue(routingKey, this, arguments); + if (isDurable() && exchange.isDurable()) + { + _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments); + } + + _bindings.addBinding(routingKey, arguments, exchange); + } + + public void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + exchange.deregisterQueue(routingKey, this, arguments); + if (isDurable() && exchange.isDurable()) + { + _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments); + } + + boolean removed = _bindings.remove(routingKey, arguments, exchange); + if (!removed) + { + _logger.error("Mismatch between queue bindings and exchange record of bindings"); + } + } + + public List getExchangeBindings() + { + return new ArrayList(_bindings.getExchangeBindings()); + } + + // ------ Manage Subscriptions + + public synchronized void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException + { + + if (isExclusiveSubscriber()) + { + throw new ExistingExclusiveSubscription(); + } + + if (exclusive) + { + if (getConsumerCount() != 0) + { + throw new ExistingSubscriptionPreventsExclusive(); + } + else + { + _exclusiveSubscriber = subscription; + + } + } + + _activeSubscriberCount.incrementAndGet(); + subscription.setStateListener(this); + subscription.setLastSeenEntry(null, _entries.getHead()); + + if (!isDeleted()) + { + subscription.setQueue(this); + _subscriptionList.add(subscription); + if (isDeleted()) + { + subscription.queueDeleted(this); + } + } + else + { + // TODO + } + + deliverAsync(subscription); + + } + + public synchronized void unregisterSubscription(final Subscription subscription) throws AMQException + { + if (subscription == null) + { + throw new NullPointerException("subscription argument is null"); + } + + boolean removed = _subscriptionList.remove(subscription); + + if (removed) + { + subscription.close(); + // No longer can the queue have an exclusive consumer + setExclusiveSubscriber(null); + + QueueEntry lastSeen; + + while ((lastSeen = subscription.getLastSeenEntry()) != null) + { + subscription.setLastSeenEntry(lastSeen, null); + } + + // auto-delete queues must be deleted if there are no remaining subscribers + + if (_autoDelete && getConsumerCount() == 0) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Auto-deleteing queue:" + this); + } + + delete(); + + // we need to manually fire the event to the removed subscription (which was the last one left for this + // queue. This is because the delete method uses the subscription set which has just been cleared + subscription.queueDeleted(this); + } + } + + } + + // ------ Enqueue / Dequeue + + public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException + { + + incrementQueueCount(); + incrementQueueSize(message); + + _totalMessagesReceived.incrementAndGet(); + + QueueEntry entry; + Subscription exclusiveSub = _exclusiveSubscriber; + + if (exclusiveSub != null) + { + exclusiveSub.getSendLock(); + + try + { + entry = _entries.add(message); + + deliverToSubscription(exclusiveSub, entry); + + // where there is more than one producer there's a reasonable chance that even though there is + // no "queueing" we do not deliver because we get an interleving of _entries.add and + // deliverToSubscription between threads. Therefore have one more try. + if (!(entry.isAcquired() || entry.isDeleted())) + { + deliverToSubscription(exclusiveSub, entry); + } + } + finally + { + exclusiveSub.releaseSendLock(); + } + } + else + { + entry = _entries.add(message); + /* + + iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message + + */ + SubscriptionList.SubscriptionNode node = _lastSubscriptionNode.get(); + SubscriptionList.SubscriptionNode nextNode = node.getNext(); + if (nextNode == null) + { + nextNode = _subscriptionList.getHead().getNext(); + } + while (nextNode != null) + { + if (_lastSubscriptionNode.compareAndSet(node, nextNode)) + { + break; + } + else + { + node = _lastSubscriptionNode.get(); + nextNode = node.getNext(); + if (nextNode == null) + { + nextNode = _subscriptionList.getHead().getNext(); + } + } + } + + // always do one extra loop after we believe we've finished + // this catches the case where we *just* miss an update + int loops = 2; + + while (!(entry.isAcquired() || entry.isDeleted()) && loops != 0) + { + if (nextNode == null) + { + loops--; + nextNode = _subscriptionList.getHead(); + } + else + { + // if subscription at end, and active, offer + Subscription sub = nextNode.getSubscription(); + deliverToSubscription(sub, entry); + } + nextNode = nextNode.getNext(); + + } + } + + if (entry.immediateAndNotDelivered()) + { + dequeue(storeContext, entry); + entry.dispose(storeContext); + } + else if (!(entry.isAcquired() || entry.isDeleted())) + { + checkSubscriptionsNotAheadOfDelivery(entry); + + deliverAsync(); + } + + try + { + _managedObject.checkForNotification(entry.getMessage()); + } + catch (JMException e) + { + throw new AMQException("Unable to get notification from manage queue: " + e, e); + } + + return entry; + + } + + private void deliverToSubscription(final Subscription sub, final QueueEntry entry) + throws AMQException + { + + sub.getSendLock(); + try + { + if (subscriptionReadyAndHasInterest(sub, entry) + && !sub.isSuspended()) + { + if (!sub.wouldSuspend(entry)) + { + if (!sub.isBrowser() && !entry.acquire(sub)) + { + // restore credit here that would have been taken away by wouldSuspend since we didn't manage + // to acquire the entry for this subscription + sub.restoreCredit(entry); + } + else + { + + deliverMessage(sub, entry); + + } + } + } + } + finally + { + sub.releaseSendLock(); + } + } + + protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + { + // This method is only required for queues which mess with ordering + // Simple Queues don't :-) + } + + private void incrementQueueSize(final AMQMessage message) + { + getAtomicQueueSize().addAndGet(message.getSize()); + } + + private void incrementQueueCount() + { + getAtomicQueueCount().incrementAndGet(); + } + + private void deliverMessage(final Subscription sub, final QueueEntry entry) + throws AMQException + { + _deliveredMessages.incrementAndGet(); + sub.send(entry); + + } + + private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry) + { + + // We need to move this subscription on, past entries which are already acquired, or deleted or ones it has no + // interest in. + QueueEntry node = sub.getLastSeenEntry(); + while (node != null && (node.isAcquired() || node.isDeleted() || !sub.hasInterest(node))) + { + + QueueEntry newNode = _entries.next(node); + if (newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + else + { + node = null; + break; + } + + } + + if (node == entry) + { + // If the first entry that subscription can process is the one we are trying to deliver to it, then we are + // good + return true; + } + else + { + // Otherwise we should try to update the subscription's last seen entry to the entry we got to, providing + // no-one else has updated it to something furhter on in the list + //TODO - check + //updateLastSeenEntry(sub, entry); + return false; + } + + } + + private void updateLastSeenEntry(final Subscription sub, final QueueEntry entry) + { + QueueEntry node = sub.getLastSeenEntry(); + + if (node != null && entry.compareTo(node) < 0 && sub.hasInterest(entry)) + { + do + { + if (sub.setLastSeenEntry(node, entry)) + { + return; + } + else + { + node = sub.getLastSeenEntry(); + } + } + while (node != null && entry.compareTo(node) < 0); + } + + } + + public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException + { + + SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards + while (subscriberIter.advance()) + { + Subscription sub = subscriberIter.getNode().getSubscription(); + + // we don't make browsers send the same stuff twice + if (!sub.isBrowser()) + { + updateLastSeenEntry(sub, entry); + } + } + + deliverAsync(); + + } + + public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException + { + decrementQueueCount(); + decrementQueueSize(entry); + if (entry.acquiredBySubscription()) + { + _deliveredMessages.decrementAndGet(); + } + + try + { + AMQMessage msg = entry.getMessage(); + if (msg.isPersistent()) + { + _virtualHost.getMessageStore().dequeueMessage(storeContext, this, msg.getMessageId()); + } + //entry.dispose(storeContext); + + } + catch (MessageCleanupException e) + { + // Message was dequeued, but could not then be deleted + // though it is no longer referenced. This should be very + // rare and can be detected and cleaned up on recovery or + // done through some form of manual intervention. + _logger.error(e, e); + } + catch (AMQException e) + { + throw new FailedDequeueException(_name.toString(), e); + } + + } + + private void decrementQueueSize(final QueueEntry entry) + { + getAtomicQueueSize().addAndGet(-entry.getMessage().getSize()); + } + + void decrementQueueCount() + { + getAtomicQueueCount().decrementAndGet(); + } + + public boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException + { + /* TODO : This is wrong as the subscription may be suspended, we should instead change the state of the message + entry to resend and move back the subscription pointer. */ + + subscription.getSendLock(); + try + { + if (!subscription.isClosed()) + { + deliverMessage(subscription, entry); + return true; + } + else + { + return false; + } + } + finally + { + subscription.releaseSendLock(); + } + } + + public int getConsumerCount() + { + return _subscriptionList.size(); + } + + public int getActiveConsumerCount() + { + return _activeSubscriberCount.get(); + } + + public boolean isUnused() + { + return getConsumerCount() == 0; + } + + public boolean isEmpty() + { + return getMessageCount() == 0; + } + + public int getMessageCount() + { + return getAtomicQueueCount().get(); + } + + public long getQueueDepth() + { + return getAtomicQueueSize().get(); + } + + public int getUndeliveredMessageCount() + { + int count = getMessageCount() - _deliveredMessages.get(); + if (count < 0) + { + return 0; + } + else + { + return count; + } + } + + public long getReceivedMessageCount() + { + return _totalMessagesReceived.get(); + } + + public long getOldestMessageArrivalTime() + { + QueueEntry entry = getOldestQueueEntry(); + return entry == null ? Long.MAX_VALUE : entry.getMessage().getArrivalTime(); + } + + protected QueueEntry getOldestQueueEntry() + { + return _entries.next(_entries.getHead()); + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + public List getMessagesOnTheQueue() + { + ArrayList entryList = new ArrayList(); + QueueEntryIterator queueListIterator = _entries.iterator(); + while (queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if (node != null && !node.isDeleted()) + { + entryList.add(node); + } + } + return entryList; + + } + + public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState) + { + if (oldState == Subscription.State.ACTIVE && newState != Subscription.State.ACTIVE) + { + _activeSubscriberCount.decrementAndGet(); + + } + else if (newState == Subscription.State.ACTIVE) + { + if (oldState != Subscription.State.ACTIVE) + { + _activeSubscriberCount.incrementAndGet(); + + } + deliverAsync(sub); + } + } + + public int compareTo(final AMQQueue o) + { + return _name.compareTo(o.getName()); + } + + public AtomicInteger getAtomicQueueCount() + { + return _atomicQueueCount; + } + + public AtomicLong getAtomicQueueSize() + { + return _atomicQueueSize; + } + + private boolean isExclusiveSubscriber() + { + return _exclusiveSubscriber != null; + } + + private void setExclusiveSubscriber(Subscription exclusiveSubscriber) + { + _exclusiveSubscriber = exclusiveSubscriber; + } + + public static interface QueueEntryFilter + { + public boolean accept(QueueEntry entry); + + public boolean filterComplete(); + } + + public List getMessagesOnTheQueue(final long fromMessageId, final long toMessageId) + { + return getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + return messageId >= fromMessageId && messageId <= toMessageId; + } + + public boolean filterComplete() + { + return false; + } + }); + } + + public QueueEntry getMessageOnTheQueue(final long messageId) + { + List entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + private boolean _complete; + + public boolean accept(QueueEntry entry) + { + _complete = entry.getMessage().getMessageId() == messageId; + return _complete; + } + + public boolean filterComplete() + { + return _complete; + } + }); + return entries.isEmpty() ? null : entries.get(0); + } + + public List getMessagesOnTheQueue(QueueEntryFilter filter) + { + ArrayList entryList = new ArrayList(); + QueueEntryIterator queueListIterator = _entries.iterator(); + while (queueListIterator.advance() && !filter.filterComplete()) + { + QueueEntry node = queueListIterator.getNode(); + if (!node.isDeleted() && filter.accept(node)) + { + entryList.add(node); + } + } + return entryList; + + } + + public void moveMessagesToAnotherQueue(final long fromMessageId, + final long toMessageId, + String queueName, + StoreContext storeContext) + { + + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + MessageStore store = getVirtualHost().getMessageStore(); + + List entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + return (messageId >= fromMessageId) + && (messageId <= toMessageId) + && entry.acquire(); + } + + public boolean filterComplete() + { + return false; + } + }); + + try + { + store.beginTran(storeContext); + + // Move the messages in on the message store. + for (QueueEntry entry : entries) + { + AMQMessage message = entry.getMessage(); + + if (message.isPersistent() && toQueue.isDurable()) + { + store.enqueueMessage(storeContext, toQueue, message.getMessageId()); + } + // dequeue does not decrement the refence count + entry.dequeue(storeContext); + } + + // Commit and flush the move transcations. + try + { + store.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + } + catch (AMQException e) + { + try + { + store.abortTran(storeContext); + } + catch (AMQException rollbackEx) + { + _logger.error("Failed to rollback transaction when error occured moving messages", rollbackEx); + } + throw new RuntimeException(e); + } + + try + { + for (QueueEntry entry : entries) + { + toQueue.enqueue(storeContext, entry.getMessage()); + + } + } + catch (MessageCleanupException e) + { + throw new RuntimeException(e); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + } + + public void copyMessagesToAnotherQueue(final long fromMessageId, + final long toMessageId, + String queueName, + final StoreContext storeContext) + { + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + MessageStore store = getVirtualHost().getMessageStore(); + + List entries = getMessagesOnTheQueue(new QueueEntryFilter() + { + + public boolean accept(QueueEntry entry) + { + final long messageId = entry.getMessage().getMessageId(); + if ((messageId >= fromMessageId) + && (messageId <= toMessageId)) + { + if (!entry.isDeleted()) + { + return entry.getMessage().incrementReference(); + } + } + + return false; + } + + public boolean filterComplete() + { + return false; + } + }); + + try + { + store.beginTran(storeContext); + + // Move the messages in on the message store. + for (QueueEntry entry : entries) + { + AMQMessage message = entry.getMessage(); + + if (message.isReferenced() && message.isPersistent() && toQueue.isDurable()) + { + store.enqueueMessage(storeContext, toQueue, message.getMessageId()); + } + } + + // Commit and flush the move transcations. + try + { + store.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + } + catch (AMQException e) + { + try + { + store.abortTran(storeContext); + } + catch (AMQException rollbackEx) + { + _logger.error("Failed to rollback transaction when error occured moving messages", rollbackEx); + } + throw new RuntimeException(e); + } + + try + { + for (QueueEntry entry : entries) + { + if (entry.getMessage().isReferenced()) + { + toQueue.enqueue(storeContext, entry.getMessage()); + } + } + } + catch (MessageCleanupException e) + { + throw new RuntimeException(e); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + } + + public void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) + { + + try + { + QueueEntryIterator queueListIterator = _entries.iterator(); + + while (queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + + final long messageId = node.getMessage().getMessageId(); + + if ((messageId >= fromMessageId) + && (messageId <= toMessageId) + && !node.isDeleted() + && node.acquire()) + { + node.discard(storeContext); + } + + } + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + + } + + // ------ Management functions + + public void deleteMessageFromTop(StoreContext storeContext) throws AMQException + { + QueueEntryIterator queueListIterator = _entries.iterator(); + boolean noDeletes = true; + + while (noDeletes && queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if (!node.isDeleted() && node.acquire()) + { + node.discard(storeContext); + noDeletes = false; + } + + } + } + + public long clearQueue(StoreContext storeContext) throws AMQException + { + + QueueEntryIterator queueListIterator = _entries.iterator(); + long count = 0; + + while (queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if (!node.isDeleted() && node.acquire()) + { + node.discard(storeContext); + count++; + } + + } + return count; + + } + + public void addQueueDeleteTask(final Task task) + { + _deleteTaskList.add(task); + } + + public int delete() throws AMQException + { + if (!_deleted.getAndSet(true)) + { + + SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + + while (subscriptionIter.advance()) + { + Subscription s = subscriptionIter.getNode().getSubscription(); + if (s != null) + { + s.queueDeleted(this); + } + } + + _bindings.deregister(); + _virtualHost.getQueueRegistry().unregisterQueue(_name); + + _managedObject.unregister(); + for (Task task : _deleteTaskList) + { + task.doTask(this); + } + + _deleteTaskList.clear(); + stop(); + } + return getMessageCount(); + + } + + public void stop() + { + if (!_stopped.getAndSet(true)) + { + ReferenceCountingExecutorService.getInstance().releaseExecutorService(); + } + } + + public void deliverAsync() + { + _stateChangeCount.incrementAndGet(); + + Runner runner = new Runner(); + + if (_asynchronousRunner.compareAndSet(null, runner)) + { + _asyncDelivery.execute(runner); + } + } + + public void deliverAsync(Subscription sub) + { + _asyncDelivery.execute(new SubFlushRunner(sub)); + } + + private class Runner implements ReadWriteRunnable + { + public void run() + { + try + { + processQueue(this); + } + catch (AMQException e) + { + _logger.error(e); + } + + } + + public boolean isRead() + { + return false; + } + + public boolean isWrite() + { + return true; + } + } + + private class SubFlushRunner implements ReadWriteRunnable + { + private final Subscription _sub; + + public SubFlushRunner(Subscription sub) + { + _sub = sub; + } + + public void run() + { + boolean complete = false; + try + { + complete = flushSubscription(_sub, new Long(MAX_ASYNC_DELIVERIES)); + + } + catch (AMQException e) + { + _logger.error(e); + } + if (!complete && !_sub.isSuspended()) + { + _asyncDelivery.execute(this); + } + + } + + public boolean isRead() + { + return false; + } + + public boolean isWrite() + { + return true; + } + } + + public void flushSubscription(Subscription sub) throws AMQException + { + flushSubscription(sub, Long.MAX_VALUE); + } + + public boolean flushSubscription(Subscription sub, Long iterations) throws AMQException + { + boolean atTail = false; + + while (!sub.isSuspended() && !atTail && iterations != 0) + { + try + { + sub.getSendLock(); + atTail = attemptDelivery(sub); + if (atTail && sub.isAutoClose()) + { + unregisterSubscription(sub); + + ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag()); + } + else if (!atTail) + { + iterations--; + } + } + finally + { + sub.releaseSendLock(); + } + } + + // if there's (potentially) more than one subscription the others will potentially not have been advanced to the + // next entry they are interested in yet. This would lead to holding on to references to expired messages, etc + // which would give us memory "leak". + + if (!isExclusiveSubscriber()) + { + advanceAllSubscriptions(); + } + return atTail; + } + + private boolean attemptDelivery(Subscription sub) throws AMQException + { + boolean atTail = false; + boolean advanced = false; + boolean subActive = sub.isActive(); + if (subActive) + { + QueueEntry node = moveSubscriptionToNextNode(sub); + if (!(node.isAcquired() || node.isDeleted())) + { + if (!sub.isSuspended()) + { + if (sub.hasInterest(node)) + { + if (!sub.wouldSuspend(node)) + { + if (!sub.isBrowser() && !node.acquire(sub)) + { + sub.restoreCredit(node); + } + else + { + deliverMessage(sub, node); + + if (sub.isBrowser()) + { + QueueEntry newNode = _entries.next(node); + + if (newNode != null) + { + advanced = true; + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + } + } + + } + else // Not enough Credit for message and wouldSuspend + { + //QPID-1187 - Treat the subscription as suspended for this message + // and wait for the message to be removed to continue delivery. + subActive = false; + node.addStateChangeListener(new QueueEntryListener(sub, node)); + } + } + else + { + // this subscription is not interested in this node so we can skip over it + QueueEntry newNode = _entries.next(node); + if (newNode != null) + { + sub.setLastSeenEntry(node, newNode); + } + } + } + + } + atTail = (_entries.next(node) == null) && !advanced; + } + return atTail || !subActive; + } + + protected void advanceAllSubscriptions() throws AMQException + { + SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + while (subscriberIter.advance()) + { + SubscriptionList.SubscriptionNode subNode = subscriberIter.getNode(); + Subscription sub = subNode.getSubscription(); + moveSubscriptionToNextNode(sub); + } + } + + private QueueEntry moveSubscriptionToNextNode(final Subscription sub) + throws AMQException + { + QueueEntry node = sub.getLastSeenEntry(); + + while (node != null && (node.isAcquired() || node.isDeleted() || node.expired())) + { + if (!node.isAcquired() && !node.isDeleted() && node.expired()) + { + if (node.acquire()) + { + final StoreContext reapingStoreContext = new StoreContext(); + node.discard(reapingStoreContext); + } + } + QueueEntry newNode = _entries.next(node); + if (newNode != null) + { + sub.setLastSeenEntry(node, newNode); + node = sub.getLastSeenEntry(); + } + else + { + break; + } + + } + return node; + } + + private void processQueue(Runnable runner) throws AMQException + { + long stateChangeCount; + long previousStateChangeCount = Long.MIN_VALUE; + boolean deliveryIncomplete = true; + + int extraLoops = 1; + Long iterations = new Long(MAX_ASYNC_DELIVERIES); + + _asynchronousRunner.compareAndSet(runner, null); + + while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete) && _asynchronousRunner.compareAndSet(null, runner)) + { + // we want to have one extra loop after every subscription has reached the point where it cannot move + // further, just in case the advance of one subscription in the last loop allows a different subscription to + // move forward in the next iteration + + if (previousStateChangeCount != stateChangeCount) + { + extraLoops = 1; + } + + previousStateChangeCount = stateChangeCount; + deliveryIncomplete = _subscriptionList.size() != 0; + boolean done = true; + + SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + //iterate over the subscribers and try to advance their pointer + while (subscriptionIter.advance()) + { + boolean closeConsumer = false; + Subscription sub = subscriptionIter.getNode().getSubscription(); + sub.getSendLock(); + try + { + if (sub != null) + { + + QueueEntry node = moveSubscriptionToNextNode(sub); + if (node != null) + { + done = attemptDelivery(sub); + } + } + if (done) + { + if (extraLoops == 0) + { + deliveryIncomplete = false; + if (sub.isAutoClose()) + { + unregisterSubscription(sub); + + ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag()); + } + } + else + { + extraLoops--; + } + } + else + { + iterations--; + extraLoops = 1; + } + } + finally + { + sub.releaseSendLock(); + } + } + _asynchronousRunner.set(null); + } + + // If deliveries == 0 then the limitting factor was the time-slicing rather than available messages or credit + // therefore we should schedule this runner again (unless someone beats us to it :-) ). + if (iterations == 0 && _asynchronousRunner.compareAndSet(null, runner)) + { + _asyncDelivery.execute(runner); + } + } + + public void removeExpiredIfNoSubscribers() throws AMQException + { + + final StoreContext storeContext = new StoreContext(); + + QueueEntryIterator queueListIterator = _entries.iterator(); + + while (queueListIterator.advance()) + { + QueueEntry node = queueListIterator.getNode(); + if (!node.isDeleted() && node.expired() && node.acquire()) + { + + node.discard(storeContext); + } + + } + + } + + public long getMinimumAlertRepeatGap() + { + return _minimumAlertRepeatGap; + } + + public void setMinimumAlertRepeatGap(long minimumAlertRepeatGap) + { + _minimumAlertRepeatGap = minimumAlertRepeatGap; + } + + public long getMaximumMessageAge() + { + return _maximumMessageAge; + } + + public void setMaximumMessageAge(long maximumMessageAge) + { + _maximumMessageAge = maximumMessageAge; + if (maximumMessageAge == 0L) + { + _notificationChecks.remove(NotificationCheck.MESSAGE_AGE_ALERT); + } + else + { + _notificationChecks.add(NotificationCheck.MESSAGE_AGE_ALERT); + } + } + + public long getMaximumMessageCount() + { + return _maximumMessageCount; + } + + public void setMaximumMessageCount(final long maximumMessageCount) + { + _maximumMessageCount = maximumMessageCount; + if (maximumMessageCount == 0L) + { + _notificationChecks.remove(NotificationCheck.MESSAGE_COUNT_ALERT); + } + else + { + _notificationChecks.add(NotificationCheck.MESSAGE_COUNT_ALERT); + } + + } + + public long getMaximumQueueDepth() + { + return _maximumQueueDepth; + } + + // Sets the queue depth, the max queue size + public void setMaximumQueueDepth(final long maximumQueueDepth) + { + _maximumQueueDepth = maximumQueueDepth; + if (maximumQueueDepth == 0L) + { + _notificationChecks.remove(NotificationCheck.QUEUE_DEPTH_ALERT); + } + else + { + _notificationChecks.add(NotificationCheck.QUEUE_DEPTH_ALERT); + } + + } + + public long getMaximumMessageSize() + { + return _maximumMessageSize; + } + + public void setMaximumMessageSize(final long maximumMessageSize) + { + _maximumMessageSize = maximumMessageSize; + if (maximumMessageSize == 0L) + { + _notificationChecks.remove(NotificationCheck.MESSAGE_SIZE_ALERT); + } + else + { + _notificationChecks.add(NotificationCheck.MESSAGE_SIZE_ALERT); + } + } + + public Set getNotificationChecks() + { + return _notificationChecks; + } + + public ManagedObject getManagedObject() + { + return _managedObject; + } + + private final class QueueEntryListener implements QueueEntry.StateChangeListener + { + private final QueueEntry _entry; + private final Subscription _sub; + + public QueueEntryListener(final Subscription sub, final QueueEntry entry) + { + _entry = entry; + _sub = sub; + } + + public boolean equals(Object o) + { + return _entry == ((QueueEntryListener) o)._entry && _sub == ((QueueEntryListener) o)._sub; + } + + public int hashCode() + { + return System.identityHashCode(_entry) ^ System.identityHashCode(_sub); + } + + public void stateChanged(QueueEntry entry, QueueEntry.State oldSate, QueueEntry.State newState) + { + entry.removeStateChangeListener(this); + deliverAsync(_sub); + } + } + + public List getMessagesOnTheQueue(int num) + { + return getMessagesOnTheQueue(num, 0); + } + + public List getMessagesOnTheQueue(int num, int offset) + { + ArrayList ids = new ArrayList(num); + QueueEntryIterator it = _entries.iterator(); + for (int i = 0; i < offset; i++) + { + it.advance(); + } + + for (int i = 0; i < num && !it.atTail(); i++) + { + it.advance(); + ids.add(it.getNode().getMessage().getMessageId()); + } + return ids; + } + + public void configure(Configuration queueConfiguration) + { + Configurator.configure(this, queueConfiguration); + resetNotifications(); + } +} \ No newline at end of file diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java new file mode 100644 index 0000000000..a46c5ae2e8 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -0,0 +1,178 @@ +package org.apache.qpid.server.queue; + +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 SimpleQueueEntryList implements QueueEntryList +{ + private final QueueEntryImpl _head; + + private volatile QueueEntryImpl _tail; + + static final AtomicReferenceFieldUpdater + _tailUpdater = + AtomicReferenceFieldUpdater.newUpdater + (SimpleQueueEntryList.class, QueueEntryImpl.class, "_tail"); + + + private final AMQQueue _queue; + + static final AtomicReferenceFieldUpdater + _nextUpdater = + AtomicReferenceFieldUpdater.newUpdater + (QueueEntryImpl.class, QueueEntryImpl.class, "_next"); + + + + + + public SimpleQueueEntryList(AMQQueue queue) + { + _queue = queue; + _head = new QueueEntryImpl(this); + _tail = _head; + } + + void advanceHead() + { + QueueEntryImpl head = _head.nextNode(); + while(head._next != null && head.isDeleted()) + { + + final QueueEntryImpl newhead = head.nextNode(); + if(newhead != null) + { + _nextUpdater.compareAndSet(_head,head, newhead); + } + head = _head.nextNode(); + } + } + + + public AMQQueue getQueue() + { + return _queue; + } + + + public QueueEntry add(AMQMessage message) + { + QueueEntryImpl node = new QueueEntryImpl(this, message); + for (;;) + { + QueueEntryImpl tail = _tail; + QueueEntryImpl next = tail.nextNode(); + if (tail == _tail) + { + if (next == null) + { + node.setEntryId(tail.getEntryId()+1); + if (_nextUpdater.compareAndSet(tail, null, node)) + { + _tailUpdater.compareAndSet(this, tail, node); + + return node; + } + } + else + { + _tailUpdater.compareAndSet(this,tail, next); + } + } + } + } + + public QueueEntry next(QueueEntry node) + { + return ((QueueEntryImpl)node).getNext(); + } + + + public class QueueEntryIteratorImpl implements QueueEntryIterator + { + + private QueueEntryImpl _lastNode; + + QueueEntryIteratorImpl(QueueEntryImpl startNode) + { + _lastNode = startNode; + } + + + public boolean atTail() + { + return _lastNode.nextNode() == null; + } + + public QueueEntry getNode() + { + + return _lastNode; + + } + + public boolean advance() + { + + if(!atTail()) + { + QueueEntryImpl nextNode = _lastNode.nextNode(); + while(nextNode.isDeleted() && nextNode.nextNode() != null) + { + nextNode = nextNode.nextNode(); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + + } + + } + + + public QueueEntryIterator iterator() + { + return new QueueEntryIteratorImpl(_head); + } + + + public QueueEntry getHead() + { + return _head; + } + + static class Factory implements QueueEntryListFactory + { + + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + return new SimpleQueueEntryList(queue); + } + } + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java new file mode 100644 index 0000000000..9b91c71a1d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java @@ -0,0 +1,127 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import java.util.LinkedList; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentHeaderBody; + +/** + * Contains data that is only used in AMQMessage transiently, e.g. while the content + * body fragments are arriving. + * + * Having this data stored in a separate class means that the AMQMessage class avoids + * the small overhead of numerous guaranteed-null references. + * + * @author Apache Software Foundation + */ +public class TransientMessageData +{ + /** + * Stored temporarily until the header has been received at which point it is used when + * constructing the handle + */ + private MessagePublishInfo _messagePublishInfo; + + /** + * Also stored temporarily. + */ + private ContentHeaderBody _contentHeaderBody; + + /** + * Keeps a track of how many bytes we have received in body frames + */ + private long _bodyLengthReceived = 0; + + /** + * This is stored during routing, to know the queues to which this message should immediately be + * delivered. It is cleared after delivery has been attempted. Any persistent record of destinations is done + * by the message handle. + */ + private List _destinationQueues; + + public MessagePublishInfo getMessagePublishInfo() + { + return _messagePublishInfo; + } + + public void setMessagePublishInfo(MessagePublishInfo messagePublishInfo) + { + _messagePublishInfo = messagePublishInfo; + } + + public List getDestinationQueues() + { + return _destinationQueues == null ? (List) Collections.EMPTY_LIST : _destinationQueues; + } + + public void setDestinationQueues(List destinationQueues) + { + _destinationQueues = destinationQueues; + } + + public ContentHeaderBody getContentHeaderBody() + { + return _contentHeaderBody; + } + + public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) + { + _contentHeaderBody = contentHeaderBody; + } + + public long getBodyLengthReceived() + { + return _bodyLengthReceived; + } + + public void addBodyLength(int value) + { + _bodyLengthReceived += value; + } + + public boolean isAllContentReceived() throws AMQException + { + return _bodyLengthReceived == _contentHeaderBody.bodySize; + } + + public void addDestinationQueue(AMQQueue queue) + { + if(_destinationQueues == null) + { + _destinationQueues = new ArrayList(); + } + _destinationQueues.add(queue); + } + + public boolean isPersistent() + { + //todo remove literal values to a constant file such as AMQConstants in common + return _contentHeaderBody.properties instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) _contentHeaderBody.properties).getDeliveryMode() == 2; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/UnauthorizedAccessException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/UnauthorizedAccessException.java new file mode 100644 index 0000000000..295cb266b9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/UnauthorizedAccessException.java @@ -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. + * + */ + +package org.apache.qpid.server.queue; + +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.RequiredDeliveryException; + +/** + * UnauthorizedAccessException is a {@link RequiredDeliveryException} that represents the failure case where a message + * is published with a user id different from the one used when creating the connection . + * The AMQP status code, 403, is always used to report this condition. + * + */ + +public class UnauthorizedAccessException extends RequiredDeliveryException +{ + public UnauthorizedAccessException(String msg, AMQMessage amqMessage) + { + super(msg, amqMessage); + } + + public AMQConstant getReplyCode() + { + return AMQConstant.ACCESS_REFUSED; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java new file mode 100644 index 0000000000..3ed8b0e55c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java @@ -0,0 +1,219 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; + +/** + * @author Robert Greig (robert.j.greig@jpmorgan.com) + */ +public class WeakReferenceMessageHandle implements AMQMessageHandle +{ + private WeakReference _contentHeaderBody; + + private WeakReference _messagePublishInfo; + + private List> _contentBodies; + + private boolean _redelivered; + + private final MessageStore _messageStore; + + private final Long _messageId; + private long _arrivalTime; + + public WeakReferenceMessageHandle(final Long messageId, MessageStore messageStore) + { + _messageId = messageId; + _messageStore = messageStore; + } + + public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException + { + ContentHeaderBody chb = (_contentHeaderBody != null ? _contentHeaderBody.get() : null); + if (chb == null) + { + MessageMetaData mmd = loadMessageMetaData(context); + chb = mmd.getContentHeaderBody(); + } + return chb; + } + + public Long getMessageId() + { + return _messageId; + } + + private MessageMetaData loadMessageMetaData(StoreContext context) + throws AMQException + { + MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId); + populateFromMessageMetaData(mmd); + return mmd; + } + + private void populateFromMessageMetaData(MessageMetaData mmd) + { + _arrivalTime = mmd.getArrivalTime(); + _contentHeaderBody = new WeakReference(mmd.getContentHeaderBody()); + _messagePublishInfo = new WeakReference(mmd.getMessagePublishInfo()); + } + + public int getBodyCount(StoreContext context) throws AMQException + { + if (_contentBodies == null) + { + MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId); + int chunkCount = mmd.getContentChunkCount(); + _contentBodies = new ArrayList>(chunkCount); + for (int i = 0; i < chunkCount; i++) + { + _contentBodies.add(new WeakReference(null)); + } + } + return _contentBodies.size(); + } + + public long getBodySize(StoreContext context) throws AMQException + { + return getContentHeaderBody(context).bodySize; + } + + public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException + { + if (index > _contentBodies.size() - 1) + { + throw new IllegalArgumentException("Index " + index + " out of valid range 0 to " + + (_contentBodies.size() - 1)); + } + WeakReference wr = _contentBodies.get(index); + ContentChunk cb = wr.get(); + if (cb == null) + { + cb = _messageStore.getContentBodyChunk(context, _messageId, index); + _contentBodies.set(index, new WeakReference(cb)); + } + return cb; + } + + /** + * Content bodies are set before the publish and header frames + * + * @param storeContext + * @param contentChunk + * @param isLastContentBody + * @throws AMQException + */ + public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException + { + if (_contentBodies == null && isLastContentBody) + { + _contentBodies = new ArrayList>(1); + } + else + { + if (_contentBodies == null) + { + _contentBodies = new LinkedList>(); + } + } + _contentBodies.add(new WeakReference(contentChunk)); + _messageStore.storeContentBodyChunk(storeContext, _messageId, _contentBodies.size() - 1, + contentChunk, isLastContentBody); + } + + public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException + { + MessagePublishInfo bpb = (_messagePublishInfo != null ? _messagePublishInfo.get() : null); + if (bpb == null) + { + MessageMetaData mmd = loadMessageMetaData(context); + + bpb = mmd.getMessagePublishInfo(); + } + return bpb; + } + + public boolean isRedelivered() + { + return _redelivered; + } + + public void setRedelivered(boolean redelivered) + { + _redelivered = redelivered; + } + + public boolean isPersistent() + { + return true; + } + + /** + * This is called when all the content has been received. + * + * @param publishBody + * @param contentHeaderBody + * @throws AMQException + */ + public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo publishBody, + ContentHeaderBody contentHeaderBody) + throws AMQException + { + // if there are no content bodies the list will be null so we must + // create en empty list here + if (contentHeaderBody.bodySize == 0) + { + _contentBodies = new LinkedList>(); + } + + final long arrivalTime = System.currentTimeMillis(); + + + MessageMetaData mmd = new MessageMetaData(publishBody, contentHeaderBody, _contentBodies.size(), arrivalTime); + + _messageStore.storeMessageMetaData(storeContext, _messageId, mmd); + + + populateFromMessageMetaData(mmd); + } + + public void removeMessage(StoreContext storeContext) throws AMQException + { + _messageStore.removeMessage(storeContext, _messageId); + } + + public long getArrivalTime() + { + return _arrivalTime; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java new file mode 100644 index 0000000000..c9c3acf61b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -0,0 +1,313 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.registry; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.server.management.ManagedObjectRegistry; +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.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.database.ConfigurationFilePrincipalDatabaseManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.mina.common.IoAcceptor; + +import java.util.HashMap; +import java.util.Map; +import java.net.InetSocketAddress; + +/** + * An abstract application registry that provides access to configuration information and handles the + * construction and caching of configurable objects. + *

+ * Subclasses should handle the construction of the "registered objects" such as the exchange registry. + */ +public abstract class ApplicationRegistry implements IApplicationRegistry +{ + protected static final Logger _logger = Logger.getLogger(ApplicationRegistry.class); + + private static Map _instanceMap = new HashMap(); + + private final Map, Object> _configuredObjects = new HashMap, Object>(); + + protected final Configuration _configuration; + + public static final int DEFAULT_INSTANCE = 1; + public static final String DEFAULT_APPLICATION_REGISTRY = "org.apache.qpid.server.util.NullApplicationRegistry"; + public static String _APPLICATION_REGISTRY = DEFAULT_APPLICATION_REGISTRY; + + protected final Map _acceptors = new HashMap(); + + protected ManagedObjectRegistry _managedObjectRegistry; + + protected AuthenticationManager _authenticationManager; + + protected VirtualHostRegistry _virtualHostRegistry; + + protected ACLPlugin _accessManager; + + protected PrincipalDatabaseManager _databaseManager; + + protected PluginManager _pluginManager; + + static + { + Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService())); + } + + private static class ShutdownService implements Runnable + { + public void run() + { + removeAll(); + } + } + + public static void initialise(IApplicationRegistry instance) throws Exception + { + initialise(instance, DEFAULT_INSTANCE); + } + + public static void initialise(IApplicationRegistry instance, int instanceID) throws Exception + { + if (instance != null) + { + _logger.info("Initialising Application Registry:" + instanceID); + _instanceMap.put(instanceID, instance); + + try + { + instance.initialise(); + } + catch (Exception e) + { + _instanceMap.remove(instanceID); + throw e; + } + } + else + { + remove(instanceID); + } + } + + /** + * Method to cleanly shutdown specified registry running in this JVM + * + * @param instanceID the instance to shutdown + */ + + public static void remove(int instanceID) + { + try + { + IApplicationRegistry instance = _instanceMap.get(instanceID); + if (instance != null) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Shuting down ApplicationRegistry(" + instanceID + "):" + instance); + } + instance.close(); + } + } + catch (Exception e) + { + _logger.error("Error shutting down Application Registry(" + instanceID + "): " + e, e); + } + finally + { + _instanceMap.remove(instanceID); + } + } + + /** Method to cleanly shutdown all registries currently running in this JVM */ + public static void removeAll() + { + Object[] keys = _instanceMap.keySet().toArray(); + for (Object k : keys) + { + remove((Integer) k); + } + } + + protected ApplicationRegistry(Configuration configuration) + { + _configuration = configuration; + } + + public static IApplicationRegistry getInstance() + { + return getInstance(DEFAULT_INSTANCE); + } + + public static IApplicationRegistry getInstance(int instanceID) + { + synchronized (IApplicationRegistry.class) + { + IApplicationRegistry instance = _instanceMap.get(instanceID); + + if (instance == null) + { + try + { + _logger.info("Creating DEFAULT_APPLICATION_REGISTRY: " + _APPLICATION_REGISTRY + " : Instance:" + instanceID); + IApplicationRegistry registry = (IApplicationRegistry) Class.forName(_APPLICATION_REGISTRY).getConstructor((Class[]) null).newInstance((Object[]) null); + ApplicationRegistry.initialise(registry, instanceID); + _logger.info("Initialised Application Registry:" + instanceID); + return registry; + } + catch (Exception e) + { + _logger.error("Error configuring application: " + e, e); + //throw new AMQBrokerCreationException(instanceID, "Unable to create Application Registry instance " + instanceID); + throw new RuntimeException("Unable to create Application Registry", e); + } + } + else + { + return instance; + } + } + } + + public void close() throws Exception + { + if (_logger.isInfoEnabled()) + { + _logger.info("Shutting down ApplicationRegistry:"+this); + } + + //Stop incomming connections + unbind(); + + //Shutdown virtualhosts + for (VirtualHost virtualHost : getVirtualHostRegistry().getVirtualHosts()) + { + virtualHost.close(); + } + + // Replace above with this +// _virtualHostRegistry.close(); + +// _accessManager.close(); + +// _databaseManager.close(); + + _authenticationManager.close(); + +// _databaseManager.close(); + + // close the rmi registry(if any) started for management + if (_managedObjectRegistry != null) + { + _managedObjectRegistry.close(); + } + +// _pluginManager.close(); + } + + private void unbind() + { + synchronized (_acceptors) + { + for (InetSocketAddress bindAddress : _acceptors.keySet()) + { + IoAcceptor acceptor = _acceptors.get(bindAddress); + acceptor.unbind(bindAddress); + } + } + } + + public Configuration getConfiguration() + { + return _configuration; + } + + public void addAcceptor(InetSocketAddress bindAddress, IoAcceptor acceptor) + { + synchronized (_acceptors) + { + _acceptors.put(bindAddress, acceptor); + } + } + + public T getConfiguredObject(Class instanceType) + { + T instance = (T) _configuredObjects.get(instanceType); + if (instance == null) + { + try + { + instance = instanceType.newInstance(); + } + catch (Exception e) + { + _logger.error("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor"); + throw new IllegalArgumentException("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor", e); + } + Configurator.configure(instance); + _configuredObjects.put(instanceType, instance); + } + return instance; + } + + public static void setDefaultApplicationRegistry(String clazz) + { + _APPLICATION_REGISTRY = clazz; + } + + public VirtualHostRegistry getVirtualHostRegistry() + { + return _virtualHostRegistry; + } + + public ACLPlugin getAccessManager() + { + return _accessManager; + } + + public ManagedObjectRegistry getManagedObjectRegistry() + { + return _managedObjectRegistry; + } + + public PrincipalDatabaseManager getDatabaseManager() + { + return _databaseManager; + } + + public AuthenticationManager getAuthenticationManager() + { + return _authenticationManager; + } + + public PluginManager getPluginManager() + { + return _pluginManager; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java new file mode 100644 index 0000000000..a555b72dcf --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.registry; + +import java.io.File; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.SystemConfiguration; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.management.JMXManagedObjectRegistry; +import org.apache.qpid.server.management.ManagedObjectRegistry; +import org.apache.qpid.server.management.ManagementConfiguration; +import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.database.ConfigurationFilePrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.AMQException; + +public class ConfigurationFileApplicationRegistry extends ApplicationRegistry +{ + + public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException + { + super(config(configurationURL)); + } + + // Our configuration class needs to make the interpolate method + // public so it can be called below from the config method. + private static class MyConfiguration extends CompositeConfiguration + { + public String interpolate(String obj) + { + return super.interpolate(obj); + } + } + + private static final Configuration config(File url) throws ConfigurationException + { + // We have to override the interpolate methods so that + // interpolation takes place accross the entirety of the + // composite configuration. Without doing this each + // configuration object only interpolates variables defined + // inside itself. + final MyConfiguration conf = new MyConfiguration(); + conf.addConfiguration(new SystemConfiguration() + { + protected String interpolate(String o) + { + return conf.interpolate(o); + } + }); + conf.addConfiguration(new XMLConfiguration(url) + { + protected String interpolate(String o) + { + return conf.interpolate(o); + } + }); + return conf; + } + + public void initialise() throws Exception + { + initialiseManagedObjectRegistry(); + + _virtualHostRegistry = new VirtualHostRegistry(); + + _accessManager = ACLManager.loadACLManager("default", _configuration); + + _databaseManager = new ConfigurationFilePrincipalDatabaseManager(_configuration); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); + + _databaseManager.initialiseManagement(_configuration); + + _managedObjectRegistry.start(); + + _pluginManager = new PluginManager(_configuration.getString("plugin-directory")); + + initialiseVirtualHosts(); + + } + + private void initialiseVirtualHosts() throws Exception + { + for (String name : getVirtualHostNames()) + { + + _virtualHostRegistry.registerVirtualHost(new VirtualHost(name, getConfiguration().subset("virtualhosts.virtualhost." + name))); + } + } + + private void initialiseManagedObjectRegistry() throws AMQException + { + ManagementConfiguration config = getConfiguredObject(ManagementConfiguration.class); + if (config.enabled) + { + _managedObjectRegistry = new JMXManagedObjectRegistry(); + } + else + { + _managedObjectRegistry = new NoopManagedObjectRegistry(); + } + } + + public Collection getVirtualHostNames() + { + return getConfiguration().getList("virtualhosts.virtualhost.name"); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java new file mode 100644 index 0000000000..597ef042f9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.registry; + +import java.util.Collection; +import java.net.InetSocketAddress; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.management.ManagedObjectRegistry; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.mina.common.IoAcceptor; + +public interface IApplicationRegistry +{ + /** + * Initialise the application registry. All initialisation must be done in this method so that any components + * that need access to the application registry itself for initialisation are able to use it. Attempting to + * initialise in the constructor will lead to failures since the registry reference will not have been set. + */ + void initialise() throws Exception; + + /** + * Shutdown this Registry + * @throws Exception - //fixme needs to be made more specific + */ + void close() throws Exception; + + /** + * This gets access to a "configured object". A configured object has fields populated from a the configuration + * object (Commons Configuration) automatically, where it has the appropriate attributes defined on fields. + * Application registry implementations can choose the refresh strategy or caching approach. + * @param instanceType the type of object you want initialised. This must be unique - i.e. you can only + * have a single object of this type in the system. + * @return the configured object + */ + T getConfiguredObject(Class instanceType); + + /** + * Get the low level configuration. For use cases where the configured object approach is not required + * you can get the complete configuration information. + * @return a Commons Configuration instance + */ + Configuration getConfiguration(); + + ManagedObjectRegistry getManagedObjectRegistry(); + + PrincipalDatabaseManager getDatabaseManager(); + + AuthenticationManager getAuthenticationManager(); + + Collection getVirtualHostNames(); + + VirtualHostRegistry getVirtualHostRegistry(); + + ACLPlugin getAccessManager(); + + PluginManager getPluginManager(); + + /** + * Register any acceptors for this registry + * @param bindAddress The address that the acceptor has been bound with + * @param acceptor The acceptor in use + */ + void addAcceptor(InetSocketAddress bindAddress, IoAcceptor acceptor); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java new file mode 100644 index 0000000000..539f32a732 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLManager.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.plugins.DenyAll; +import org.apache.qpid.configuration.PropertyUtils; +import org.apache.log4j.Logger; + +import java.util.List; +import java.lang.reflect.Method; + +public class ACLManager +{ + private static final Logger _logger = Logger.getLogger(ACLManager.class); + + public static ACLPlugin loadACLManager(String name, Configuration hostConfig) throws ConfigurationException + { + ACLPlugin aclPlugin = ApplicationRegistry.getInstance().getAccessManager(); + + if (hostConfig == null) + { + _logger.warn("No Configuration specified. Using default ACLPlugin '" + aclPlugin.getPluginName() + + "' for VirtualHost:'" + name + "'"); + return aclPlugin; + } + + String accessClass = hostConfig.getString("security.access.class"); + if (accessClass == null) + { + + _logger.warn("No ACL Plugin specified. Using default ACL Plugin '" + aclPlugin.getPluginName() + + "' for VirtualHost:'" + name + "'"); + return aclPlugin; + } + + Object o; + try + { + o = Class.forName(accessClass).newInstance(); + } + catch (Exception e) + { + throw new ConfigurationException("Error initialising ACL: " + e, e); + } + + if (!(o instanceof ACLPlugin)) + { + throw new ConfigurationException("ACL Plugins must implement the ACLPlugin interface"); + } + + initialiseAccessControl((ACLPlugin) o, hostConfig); + + aclPlugin = getManager((ACLPlugin) o); + if (_logger.isInfoEnabled()) + { + _logger.info("Initialised ACL Plugin '" + aclPlugin.getPluginName() + + "' for virtualhost '" + name + "' successfully"); + } + + return aclPlugin; + } + + + private static void initialiseAccessControl(ACLPlugin accessManager, Configuration config) + throws ConfigurationException + { + //First provide the ACLPlugin with the host configuration + + accessManager.setConfiguaration(config); + + //Provide additional attribute customisation. + String baseName = "security.access.attributes.attribute."; + List argumentNames = config.getList(baseName + "name"); + List argumentValues = config.getList(baseName + "value"); + for (int i = 0; i < argumentNames.size(); i++) + { + String argName = argumentNames.get(i); + if (argName == null || argName.length() == 0) + { + throw new ConfigurationException("Access Control argument names must have length >= 1 character"); + } + if (Character.isLowerCase(argName.charAt(0))) + { + argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1); + } + String methodName = "set" + argName; + Method method = null; + try + { + method = accessManager.getClass().getMethod(methodName, String.class); + } + catch (NoSuchMethodException e) + { + //do nothing as method will be null + } + + if (method == null) + { + throw new ConfigurationException("No method " + methodName + " found in class " + accessManager.getClass() + + " hence unable to configure access control. The method must be public and " + + "have a single String argument with a void return type"); + } + try + { + method.invoke(accessManager, PropertyUtils.replaceProperties(argumentValues.get(i))); + } + catch (Exception e) + { + ConfigurationException ce = new ConfigurationException(e.getMessage(), e.getCause()); + ce.initCause(e); + throw ce; + } + } + } + + + private static ACLPlugin getManager(ACLPlugin manager) + { + if (manager == null) + { + if (ApplicationRegistry.getInstance().getAccessManager() == null) + { + return new DenyAll(); + } + else + { + return ApplicationRegistry.getInstance().getAccessManager(); + } + } + else + { + return manager; + } + } + + public static Logger getLogger() + { + return _logger; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.java new file mode 100644 index 0000000000..7855f147b4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ACLPlugin.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.security.access; + +import org.apache.qpid.framing.AMQMethodBody; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.AMQConnectionException; +import org.apache.commons.configuration.Configuration; + + +public interface ACLPlugin +{ + /** + * Pseudo-Code: + * Identify requested RighConnectiont + * Lookup users ability for that right. + * if rightsExists + * Validate right on object + * Return result + * e.g + * User, CONSUME , Queue + * User, CONSUME , Exchange + RoutingKey + * User, PUBLISH , Exchange + RoutingKey + * User, CREATE , Exchange || Queue + * User, BIND , Exchange + RoutingKey + Queue + * + * @param session - The session requesting access + * @param permission - The permission requested + * @param parameters - The above objects that are used to authorise the request. + * @return The AccessResult decision + */ + //todo potential refactor this ConnectionException Out of here + AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException; + + String getPluginName(); + + void setConfiguaration(Configuration config); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java new file mode 100644 index 0000000000..86f155d862 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access; + +public class AccessResult +{ + public enum AccessStatus + { + GRANTED, REFUSED + } + + private String _authorizer; + private AccessStatus _status; + + public AccessResult(ACLPlugin authorizer, AccessStatus status) + { + _status = status; + _authorizer = authorizer.getPluginName(); + } + + public void setAuthorizer(ACLPlugin authorizer) + { + _authorizer += authorizer.getPluginName(); + } + + public String getAuthorizer() + { + return _authorizer; + } + + public void setStatus(AccessStatus status) + { + _status = status; + } + + public AccessStatus getStatus() + { + return _status; + } + + public void addAuthorizer(ACLPlugin accessManager) + { + _authorizer = accessManager.getPluginName() + "->" + _authorizer; + } + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java new file mode 100644 index 0000000000..1b79a5a0e0 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access; + +public class AccessRights +{ + public enum Rights + { + ANY, + READ, + WRITE, + READWRITE + } + + Rights _right; + + public AccessRights(Rights right) + { + _right = right; + } + + public boolean allows(Rights rights) + { + switch (_right) + { + case ANY: + return (rights.equals(Rights.WRITE) + || rights.equals(Rights.READ) + || rights.equals(Rights.READWRITE) + || rights.equals(Rights.ANY)); + case READ: + return rights.equals(Rights.READ) || rights.equals(Rights.ANY); + case WRITE: + return rights.equals(Rights.WRITE) || rights.equals(Rights.ANY); + case READWRITE: + return true; + } + return false; + } + + public Rights getRights() + { + return _right; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java new file mode 100644 index 0000000000..f51cf24caa --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.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.security.access; + +public interface Accessable +{ + void setAccessableName(String name); + String getAccessableName(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.java new file mode 100644 index 0000000000..00757a4f8c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/Permission.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.security.access; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; + +public enum Permission +{ + CONSUME, + PUBLISH, + CREATE, + ACCESS, + BIND, + UNBIND, + DELETE, + PURGE +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java new file mode 100755 index 0000000000..23073e0613 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalPermissions.java @@ -0,0 +1,579 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.access; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.exchange.Exchange; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class PrincipalPermissions +{ + + private static final Object CONSUME_QUEUES_KEY = new Object(); + private static final Object CONSUME_TEMPORARY_KEY = new Object(); + private static final Object CONSUME_OWN_QUEUES_ONLY_KEY = new Object(); + + private static final Object CREATE_QUEUES_KEY = new Object(); + private static final Object CREATE_EXCHANGES_KEY = new Object(); + + private static final Object CREATE_QUEUE_TEMPORARY_KEY = new Object(); + private static final Object CREATE_QUEUE_QUEUES_KEY = new Object(); + private static final Object CREATE_QUEUE_EXCHANGES_KEY = new Object(); + + private static final Object CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY = new Object(); + private static final Object CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY = new Object(); + + private static final int PUBLISH_EXCHANGES_KEY = 0; + + private Map _permissions; + + private String _user; + + + public PrincipalPermissions(String user) + { + _user = user; + _permissions = new ConcurrentHashMap(); + } + + public void grant(Permission permission, Object... parameters) + { + switch (permission) + { + case ACCESS: + break; // This is a no-op as the existence of this PrincipalPermission object is scoped per VHost for ACCESS + case BIND: + break; // All the details are currently included in the create setup. + case CONSUME: // Parameters : AMQShortString queueName, Boolean Temporary, Boolean ownQueueOnly + Map consumeRights = (Map) _permissions.get(permission); + + if (consumeRights == null) + { + consumeRights = new ConcurrentHashMap(); + _permissions.put(permission, consumeRights); + } + + //if we have parametsre + if (parameters.length > 0) + { + AMQShortString queueName = (AMQShortString) parameters[0]; + Boolean temporary = (Boolean) parameters[1]; + Boolean ownQueueOnly = (Boolean) parameters[2]; + + if (temporary) + { + consumeRights.put(CONSUME_TEMPORARY_KEY, true); + } + else + { + consumeRights.put(CONSUME_TEMPORARY_KEY, false); + } + + if (ownQueueOnly) + { + consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, true); + } + else + { + consumeRights.put(CONSUME_OWN_QUEUES_ONLY_KEY, false); + } + + + LinkedList queues = (LinkedList) consumeRights.get(CONSUME_QUEUES_KEY); + if (queues == null) + { + queues = new LinkedList(); + consumeRights.put(CONSUME_QUEUES_KEY, queues); + } + + if (queueName != null) + { + queues.add(queueName); + } + } + + + break; + case CREATE: // Parameters : Boolean temporary, AMQShortString queueName + // , AMQShortString exchangeName , AMQShortString routingKey + // || AMQShortString exchangeName , AMQShortString Class + + Map createRights = (Map) _permissions.get(permission); + + if (createRights == null) + { + createRights = new ConcurrentHashMap(); + _permissions.put(permission, createRights); + + } + + //The existence of the empty map mean permission to all. + if (parameters.length == 0) + { + return; + } + + + if (parameters[0] instanceof Boolean) //Create Queue : + // Boolean temporary, [AMQShortString queueName, AMQShortString exchangeName , AMQShortString routingKey] + { + Boolean temporary = (Boolean) parameters[0]; + + AMQShortString queueName = parameters.length > 1 ? (AMQShortString) parameters[1] : null; + AMQShortString exchangeName = parameters.length > 2 ? (AMQShortString) parameters[2] : null; + //Set the routingkey to the specified value or the queueName if present + AMQShortString routingKey = parameters.length > 3 ? (AMQShortString) parameters[3] : queueName; + + // Get the queues map + Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); + + if (create_queues == null) + { + create_queues = new ConcurrentHashMap(); + createRights.put(CREATE_QUEUES_KEY, create_queues); + } + + //Allow all temp queues to be created + create_queues.put(CREATE_QUEUE_TEMPORARY_KEY, temporary); + + //Create empty list of queues + Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); + + if (create_queues_queues == null) + { + create_queues_queues = new ConcurrentHashMap(); + create_queues.put(CREATE_QUEUE_QUEUES_KEY, create_queues_queues); + } + + // We are granting CREATE rights to all temporary queues only + if (parameters.length == 1) + { + return; + } + + // if we have a queueName then we need to store any associated exchange / rk bindings + if (queueName != null) + { + Map queue = (Map) create_queues_queues.get(queueName); + if (queue == null) + { + queue = new ConcurrentHashMap(); + create_queues_queues.put(queueName, queue); + } + + if (exchangeName != null) + { + queue.put(exchangeName, routingKey); + } + + //If no exchange is specified then the presence of the queueName in the map says any exchange is ok + } + + // Store the exchange that we are being granted rights to. This will be used as part of binding + + //Lookup the list of exchanges + Map create_queues_exchanges = (Map) create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); + + if (create_queues_exchanges == null) + { + create_queues_exchanges = new ConcurrentHashMap(); + create_queues.put(CREATE_QUEUE_EXCHANGES_KEY, create_queues_exchanges); + } + + //if we have an exchange + if (exchangeName != null) + { + //Retrieve the list of permitted exchanges. + Map exchanges = (Map) create_queues_exchanges.get(exchangeName); + + if (exchanges == null) + { + exchanges = new ConcurrentHashMap(); + create_queues_exchanges.put(exchangeName, exchanges); + } + + //Store the temporary setting CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY + exchanges.put(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY, temporary); + + //Store the binding details of queue/rk for this exchange. + if (queueName != null) + { + //Retrieve the list of permitted routingKeys. + Map rKeys = (Map) exchanges.get(exchangeName); + + if (rKeys == null) + { + rKeys = new ConcurrentHashMap(); + exchanges.put(CREATE_QUEUE_EXCHANGES_ROUTINGKEYS_KEY, rKeys); + } + + rKeys.put(queueName, routingKey); + } + } + } + else // Create Exchange : AMQShortString exchangeName , AMQShortString Class + { + Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY); + + if (create_exchanges == null) + { + create_exchanges = new ConcurrentHashMap(); + createRights.put(CREATE_EXCHANGES_KEY, create_exchanges); + } + + //Should perhaps error if parameters[0] is null; + AMQShortString exchangeName = parameters.length > 0 ? (AMQShortString) parameters[0] : null; + AMQShortString className = parameters.length > 1 ? (AMQShortString) parameters[1] : null; + + //Store the exchangeName / class mapping if the mapping is null + createRights.put(exchangeName, className); + } + break; + case DELETE: + break; + + case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey + Map publishRights = (Map) _permissions.get(permission); + + if (publishRights == null) + { + publishRights = new ConcurrentHashMap(); + _permissions.put(permission, publishRights); + } + + if (parameters == null || parameters.length == 0) + { + //If we have no parameters then allow publish to all destinations + // this is signified by having a null value for publish_exchanges + } + else + { + Map publish_exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); + + if (publish_exchanges == null) + { + publish_exchanges = new ConcurrentHashMap(); + publishRights.put(PUBLISH_EXCHANGES_KEY, publish_exchanges); + } + + + HashSet routingKeys = (HashSet) publish_exchanges.get(parameters[0]); + + // Check to see if we have a routing key + if (parameters.length == 2) + { + if (routingKeys == null) + { + routingKeys = new HashSet(); + } + //Add routing key to permitted publish destinations + routingKeys.add(parameters[1]); + } + + // Add the updated routingkey list or null if all values allowed + publish_exchanges.put(parameters[0], routingKeys); + } + break; + case PURGE: + break; + case UNBIND: + break; + } + + } + + public boolean authorise(Permission permission, Object... parameters) + { + + switch (permission) + { + case ACCESS: + return true; // This is here for completeness but the SimpleXML ACLManager never calls it. + // The existence of this user specific PP can be validated in the map SimpleXML maintains. + case BIND: // Parameters : QueueBindMethod , Exchange , AMQQueue, AMQShortString routingKey + + Exchange exchange = (Exchange) parameters[1]; + + AMQQueue bind_queueName = (AMQQueue) parameters[2]; + AMQShortString routingKey = (AMQShortString) parameters[3]; + + //Get all Create Rights for this user + Map bindCreateRights = (Map) _permissions.get(Permission.CREATE); + + //Look up the Queue Creation Rights + Map bind_create_queues = (Map) bindCreateRights.get(CREATE_QUEUES_KEY); + + //Lookup the list of queues + Map bind_create_queues_queues = (Map) bindCreateRights.get(CREATE_QUEUE_QUEUES_KEY); + + // Check and see if we have a queue white list to check + if (bind_create_queues_queues != null) + { + //There a white list for queues + Map exchangeDetails = (Map) bind_create_queues_queues.get(bind_queueName); + + if (exchangeDetails == null) //Then all queue can be bound to all exchanges. + { + return true; + } + + // Check to see if we have a white list of routingkeys to check + Map rkeys = (Map) exchangeDetails.get(exchange.getName()); + + // if keys is null then any rkey is allowed on this exchange + if (rkeys == null) + { + // There is no routingkey white list + return true; + } + else + { + // We have routingKeys so a match must be found to allowed binding + Iterator keys = rkeys.keySet().iterator(); + + boolean matched = false; + while (keys.hasNext() && !matched) + { + AMQShortString rkey = (AMQShortString) keys.next(); + if (rkey.endsWith("*")) + { + matched = routingKey.startsWith(rkey.subSequence(0, rkey.length() - 1).toString()); + } + else + { + matched = routingKey.equals(rkey); + } + } + + + return matched; + } + + + } + else + { + //There a is no white list for queues + + // So can allow all queues to be bound + // but we should first check and see if we have a temp queue and validate that we are allowed + // to bind temp queues. + + //Check to see if we have a temporary queue + if (bind_queueName.isAutoDelete()) + { + // Check and see if we have an exchange white list. + Map bind_exchanges = (Map) bind_create_queues.get(CREATE_QUEUE_EXCHANGES_KEY); + + // If the exchange exists then we must check to see if temporary queues are allowed here + if (bind_exchanges != null) + { + // Check to see if the requested exchange is allowed. + Map exchangeDetails = (Map) bind_exchanges.get(exchange.getName()); + + return (Boolean) exchangeDetails.get(CREATE_QUEUE_EXCHANGES_TEMPORARY_KEY); + } + + //no white list so all allowed, drop through to return true below. + } + + // not a temporary queue and no white list so all allowed. + return true; + } + + case CREATE:// Paramters : QueueDeclareBody || ExchangeDeclareBody + + Map createRights = (Map) _permissions.get(permission); + + // If there are no create rights then deny request + if (createRights == null) + { + return false; + } + + if (parameters.length == 1) + { + if (parameters[0] instanceof QueueDeclareBody) + { + QueueDeclareBody body = (QueueDeclareBody) parameters[0]; + + //Look up the Queue Creation Rights + Map create_queues = (Map) createRights.get(CREATE_QUEUES_KEY); + + //Lookup the list of queues allowed to be created + Map create_queues_queues = (Map) create_queues.get(CREATE_QUEUE_QUEUES_KEY); + + + AMQShortString queueName = body.getQueue(); + + + if (body.getAutoDelete())// we have a temporary queue + { + return (Boolean) create_queues.get(CREATE_QUEUE_TEMPORARY_KEY); + } + else + { + // If there is a white list then check + return create_queues_queues == null || create_queues_queues.containsKey(queueName); + } + + } + else if (parameters[0] instanceof ExchangeDeclareBody) + { + ExchangeDeclareBody body = (ExchangeDeclareBody) parameters[0]; + + AMQShortString exchangeName = body.getExchange(); + + Map create_exchanges = (Map) createRights.get(CREATE_EXCHANGES_KEY); + + // If the exchange list is doesn't exist then all is allowed else check the valid exchanges + return create_exchanges == null || create_exchanges.containsKey(exchangeName); + } + } + break; + case CONSUME: // Parameters : AMQQueue + + if (parameters.length == 1 && parameters[0] instanceof AMQQueue) + { + AMQQueue queue = ((AMQQueue) parameters[0]); + Map queuePermissions = (Map) _permissions.get(permission); + + List queues = (List) queuePermissions.get(CONSUME_QUEUES_KEY); + + Boolean temporayQueues = (Boolean) queuePermissions.get(CONSUME_TEMPORARY_KEY); + Boolean ownQueuesOnly = (Boolean) queuePermissions.get(CONSUME_OWN_QUEUES_ONLY_KEY); + + // If user is allowed to publish to temporary queues and this is a temp queue then allow it. + if (temporayQueues) + { + if (queue.isAutoDelete()) + // This will allow consumption from any temporary queue including ones not owned by this user. + // Of course the exclusivity will not be broken. + { + // if not limited to ownQueuesOnly then ok else check queue Owner. + return !ownQueuesOnly || queue.getOwner().equals(_user); + } + else + { + return false; + } + } + + // if queues are white listed then ensure it is ok + if (queues != null) + { + // if no queues are listed then ALL are ok othereise it must be specified. + if (ownQueuesOnly) + { + if (queue.getOwner().equals(_user)) + { + return queues.size() == 0 || queues.contains(queue.getName()); + } + else + { + return false; + } + } + + // If we are + return queues.size() == 0 || queues.contains(queue.getName()); + } + } + + // Can't authenticate without the right parameters + return false; + case DELETE: + break; + + case PUBLISH: // Parameters : Exchange exchange, AMQShortString routingKey + Map publishRights = (Map) _permissions.get(permission); + + if (publishRights == null) + { + return false; + } + + Map exchanges = (Map) publishRights.get(PUBLISH_EXCHANGES_KEY); + + // Having no exchanges listed gives full publish rights to all exchanges + if (exchanges == null) + { + return true; + } + // Otherwise exchange must be listed in the white list + + // If the map doesn't have the exchange then it isn't allowed + if (!exchanges.containsKey(parameters[0])) + { + return false; + } + else + { + + // Get valid routing keys + HashSet routingKeys = (HashSet) exchanges.get(parameters[0]); + + // Having no routingKeys in the map then all are allowed. + if (routingKeys == null) + { + return true; + } + else + { + // We have routingKeys so a match must be found to allowed binding + Iterator keys = routingKeys.iterator(); + + + AMQShortString publishRKey = (AMQShortString)parameters[1]; + + boolean matched = false; + while (keys.hasNext() && !matched) + { + AMQShortString rkey = (AMQShortString) keys.next(); + + if (rkey.endsWith("*")) + { + matched = publishRKey.startsWith(rkey.subSequence(0, rkey.length() - 1)); + } + else + { + matched = publishRKey.equals(rkey); + } + } + return matched; + } + } + case PURGE: + break; + case UNBIND: + break; + + } + + return false; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java new file mode 100644 index 0000000000..13151a66b8 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access; + +public class VirtualHostAccess +{ + private String _vhost; + private AccessRights _rights; + + public VirtualHostAccess(String vhostaccess) + { + //format () + int hostend = vhostaccess.indexOf('('); + + if (hostend == -1) + { + throw new IllegalArgumentException("VirtualHostAccess format string contains no access _rights"); + } + + _vhost = vhostaccess.substring(0, hostend); + + String rights = vhostaccess.substring(hostend); + + if (rights.indexOf('r') != -1) + { + if (rights.indexOf('w') != -1) + { + _rights = new AccessRights(AccessRights.Rights.READWRITE); + } + else + { + _rights = new AccessRights(AccessRights.Rights.READ); + } + } + else if (rights.indexOf('w') != -1) + { + _rights = new AccessRights(AccessRights.Rights.WRITE); + } + } + + public AccessRights getAccessRights() + { + return _rights; + } + + public String getVirtualHost() + { + return _vhost; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java new file mode 100644 index 0000000000..32ec3a3bbc --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBean.java @@ -0,0 +1,473 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access.management; + +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.MBeanOperation; +import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.access.management.UserManagement; +import org.apache.log4j.Logger; +import org.apache.commons.configuration.ConfigurationException; + +import javax.management.JMException; +import javax.management.remote.JMXPrincipal; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.security.auth.login.AccountNotFoundException; +import javax.security.auth.Subject; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.FileOutputStream; +import java.util.Properties; +import java.util.List; +import java.util.Enumeration; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; +import java.security.Principal; +import java.security.AccessControlContext; +import java.security.AccessController; + +/** MBean class for AMQUserManagementMBean. It implements all the management features exposed for managing users. */ +@MBeanDescription("User Management Interface") +public class AMQUserManagementMBean extends AMQManagedObject implements UserManagement +{ + + private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class); + + private PrincipalDatabase _principalDatabase; + private String _accessFileName; + private Properties _accessRights; + // private File _accessFile; + private ReentrantLock _accessRightsUpdate = new ReentrantLock(); + + // Setup for the TabularType + static TabularType _userlistDataType; // Datatype for representing User Lists + + static CompositeType _userDataType; // Composite type for representing User + static String[] _userItemNames = {"Username", "read", "write", "admin"}; + + static + { + String[] userItemDesc = {"Broker Login username", "Management Console Read Permission", + "Management Console Write Permission", "Management Console Admin Permission"}; + + OpenType[] userItemTypes = new OpenType[4]; // User item types. + userItemTypes[0] = SimpleType.STRING; // For Username + userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read + userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write + userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin + String[] userDataIndex = {_userItemNames[0]}; + + try + { + _userDataType = + new CompositeType("User", "User Data", _userItemNames, userItemDesc, userItemTypes); + + _userlistDataType = new TabularType("Users", "List of users", _userDataType, userDataIndex); + } + catch (OpenDataException e) + { + _logger.error("Tabular data setup for viewing users incorrect."); + _userlistDataType = null; + } + } + + + public AMQUserManagementMBean() throws JMException + { + super(UserManagement.class, UserManagement.TYPE); + } + + public String getObjectInstanceName() + { + return UserManagement.TYPE; + } + + public boolean setPassword(String username, char[] password) + { + try + { + //delegate password changes to the Principal Database + return _principalDatabase.updatePassword(new UsernamePrincipal(username), password); + } + catch (AccountNotFoundException e) + { + _logger.warn("Attempt to set password of non-existant user'" + username + "'"); + return false; + } + } + + public boolean setRights(String username, boolean read, boolean write, boolean admin) + { + + if (_accessRights.get(username) == null) + { + // If the user doesn't exist in the user rights file check that they at least have an account. + if (_principalDatabase.getUser(username) == null) + { + return false; + } + } + + try + { + + _accessRightsUpdate.lock(); + + // Update the access rights + if (admin) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.ADMIN); + } + else + { + if (read | write) + { + if (read) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.READONLY); + } + if (write) + { + _accessRights.put(username, MBeanInvocationHandlerImpl.READWRITE); + } + } + else + { + _accessRights.remove(username); + } + } + + saveAccessFile(); + } + finally + { + if (_accessRightsUpdate.isHeldByCurrentThread()) + { + _accessRightsUpdate.unlock(); + } + } + + return true; + } + + public boolean createUser(String username, char[] password, boolean read, boolean write, boolean admin) + { + if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password)) + { + _accessRights.put(username, ""); + + return setRights(username, read, write, admin); + } + + return false; + } + + public boolean deleteUser(String username) + { + + try + { + if (_principalDatabase.deletePrincipal(new UsernamePrincipal(username))) + { + try + { + _accessRightsUpdate.lock(); + + _accessRights.remove(username); + saveAccessFile(); + } + finally + { + if (_accessRightsUpdate.isHeldByCurrentThread()) + { + _accessRightsUpdate.unlock(); + } + } + return true; + } + } + catch (AccountNotFoundException e) + { + _logger.warn("Attempt to delete user (" + username + ") that doesn't exist"); + } + + return false; + } + + public boolean reloadData() + { + try + { + try + { + loadAccessFile(); + } + catch (ConfigurationException e) + { + _logger.info("Reload failed due to:" + e); + return false; + } + + // Reload successful + return true; + } + catch (IOException e) + { + _logger.info("Reload failed due to:" + e); + // Reload unsuccessful + return false; + } + } + + + @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.") + public TabularData viewUsers() + { + // Table of users + // Username(string), Access rights Read,Write,Admin(bool,bool,bool) + + if (_userlistDataType == null) + { + _logger.warn("TabluarData not setup correctly"); + return null; + } + + List 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 + + String rights = (String) _accessRights.get(user.getName()); + + Boolean read = false; + Boolean write = false; + Boolean admin = false; + + if (rights != null) + { + read = rights.equals(MBeanInvocationHandlerImpl.READONLY) + || rights.equals(MBeanInvocationHandlerImpl.READWRITE); + write = rights.equals(MBeanInvocationHandlerImpl.READWRITE); + admin = rights.equals(MBeanInvocationHandlerImpl.ADMIN); + } + + Object[] itemData = {user.getName(), read, write, admin}; + CompositeData messageData = new CompositeDataSupport(_userDataType, _userItemNames, 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; + } + + /** + * setAccessFile + * + * @param accessFile the file to use for updating. + * + * @throws java.io.IOException If the file cannot be accessed + * @throws org.apache.commons.configuration.ConfigurationException + * if checks on the file fail. + */ + public void setAccessFile(String accessFile) throws IOException, ConfigurationException + { + _accessFileName = accessFile; + + if (_accessFileName != null) + { + loadAccessFile(); + } + else + { + _logger.warn("Access rights file specified is null. Access rights not changed."); + } + } + + private void loadAccessFile() throws IOException, ConfigurationException + { + try + { + _accessRightsUpdate.lock(); + + Properties accessRights = new Properties(); + + File accessFile = new File(_accessFileName); + + if (!accessFile.exists()) + { + throw new ConfigurationException("'" + _accessFileName + "' does not exist"); + } + + if (!accessFile.canRead()) + { + throw new ConfigurationException("Cannot read '" + _accessFileName + "'."); + } + + if (!accessFile.canWrite()) + { + _logger.warn("Unable to write to access file '" + _accessFileName + "' changes will not be preserved."); + } + + accessRights.load(new FileInputStream(accessFile)); + checkAccessRights(accessRights); + setAccessRights(accessRights); + } + finally + { + if (_accessRightsUpdate.isHeldByCurrentThread()) + { + _accessRightsUpdate.unlock(); + } + } + } + + private void checkAccessRights(Properties accessRights) + { + Enumeration values = accessRights.propertyNames(); + + while (values.hasMoreElements()) + { + String user = (String) values.nextElement(); + + if (_principalDatabase.getUser(user) == null) + { + _logger.warn("Access rights contains user '" + user + "' but there is no authentication data for that user"); + } + } + } + + private void saveAccessFile() + { + try + { + _accessRightsUpdate.lock(); + try + { + // remove old temporary file + File tmp = new File(_accessFileName + ".tmp"); + if (tmp.exists()) + { + tmp.delete(); + } + + //remove old backup + File old = new File(_accessFileName + ".old"); + if (old.exists()) + { + old.delete(); + } + + // Rename current file + File rights = new File(_accessFileName); + rights.renameTo(old); + + FileOutputStream output = new FileOutputStream(tmp); + _accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser()); + output.close(); + + // Rename new file to main file + tmp.renameTo(rights); + + // delete tmp + tmp.delete(); + } + catch (IOException e) + { + _logger.warn("Problem occured saving '" + _accessFileName + "' changes may not be preserved. :" + e); + } + } + finally + { + if (_accessRightsUpdate.isHeldByCurrentThread()) + { + _accessRightsUpdate.unlock(); + } + } + } + + private String getCurrentJMXUser() + { + AccessControlContext acc = AccessController.getContext(); + + Subject subject = Subject.getSubject(acc); + if (subject == null) + { + return "Unknown user, authentication Subject was null"; + } + + // Retrieve JMXPrincipal from Subject + Set principals = subject.getPrincipals(JMXPrincipal.class); + if (principals == null || principals.isEmpty()) + { + return "Unknown user principals were null"; + } + + Principal principal = principals.iterator().next(); + return principal.getName(); + } + + /** + * user=read user=write user=readwrite user=admin + * + * @param accessRights The properties list of access rights to process + */ + private void setAccessRights(Properties accessRights) + { + _logger.debug("Setting Access Rights:" + accessRights); + _accessRights = accessRights; + MBeanInvocationHandlerImpl.setAccessRights(_accessRights); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.java new file mode 100644 index 0000000000..658d7ebbd3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/management/UserManagement.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.security.access.management; + +import org.apache.qpid.server.management.MBeanOperation; +import org.apache.qpid.server.management.MBeanOperationParameter; +import org.apache.qpid.server.management.MBeanAttribute; +import org.apache.qpid.AMQException; + +import javax.management.openmbean.TabularData; +import javax.management.openmbean.CompositeData; +import javax.management.JMException; +import javax.management.MBeanOperationInfo; +import java.io.IOException; + +public interface UserManagement +{ + String TYPE = "UserManagement"; + + //********** Operations *****************// + /** + * set password for user + * + * @param username The username to create + * @param password The password for the user + * + * @return The result of the operation + */ + @MBeanOperation(name = "setPassword", description = "Set password for user.", + impact = MBeanOperationInfo.ACTION) + boolean setPassword(@MBeanOperationParameter(name = "username", description = "Username")String username, + @MBeanOperationParameter(name = "password", description = "Password")char[] password); + + /** + * set rights for users with given details + * + * @param username The username to create + * @param read The set of permission to give the new user + * @param write The set of permission to give the new user + * @param admin The set of permission to give the new user + * + * @return The result of the operation + */ + @MBeanOperation(name = "setRights", description = "Set access rights for user.", + impact = MBeanOperationInfo.ACTION) + boolean setRights(@MBeanOperationParameter(name = "username", description = "Username")String username, + @MBeanOperationParameter(name = "read", description = "Administration read")boolean read, + @MBeanOperationParameter(name = "readAndWrite", description = "Administration write")boolean write, + @MBeanOperationParameter(name = "admin", description = "Administration rights")boolean admin); + + /** + * Create users with given details + * + * @param username The username to create + * @param password The password for the user + * @param read The set of permission to give the new user + * @param write The set of permission to give the new user + * @param admin The set of permission to give the new user + * + * @return The result of the operation + */ + @MBeanOperation(name = "createUser", description = "Create new user from system.", + impact = MBeanOperationInfo.ACTION) + boolean createUser(@MBeanOperationParameter(name = "username", description = "Username")String username, + @MBeanOperationParameter(name = "password", description = "Password")char[] password, + @MBeanOperationParameter(name = "read", description = "Administration read")boolean read, + @MBeanOperationParameter(name = "readAndWrite", description = "Administration write")boolean write, + @MBeanOperationParameter(name = "admin", description = "Administration rights")boolean admin); + + /** + * View users returns all the users that are currently available to the system. + * + * @param username The user to delete + * + * @return The result of the operation + */ + @MBeanOperation(name = "deleteUser", description = "Delete user from system.", + impact = MBeanOperationInfo.ACTION) + boolean deleteUser(@MBeanOperationParameter(name = "username", description = "Username")String username); + + + /** + * Reload the date from disk + * + * @return The result of the operation + */ + @MBeanOperation(name = "reloadData", description = "Reload the authentication file from disk.", + impact = MBeanOperationInfo.ACTION) + boolean reloadData(); + + /** + * View users returns all the users that are currently available to the system. + * + * @return a table of users data (Username, read, write, admin) + */ + @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.", + impact = MBeanOperationInfo.INFO) + TabularData viewUsers(); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java new file mode 100644 index 0000000000..9b784069dd --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/AllowAll.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access.plugins; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.AccessResult; +import org.apache.qpid.server.security.access.Accessable; +import org.apache.qpid.server.security.access.Permission; +import org.apache.commons.configuration.Configuration; + +public class AllowAll implements ACLPlugin +{ + public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) + { + if (ACLManager.getLogger().isDebugEnabled()) + { + ACLManager.getLogger().debug("Allowing user:" + session.getAuthorizedID() + " for :" + permission.toString() + + " on " + body.getClass().getSimpleName() + + (parameters == null || parameters.length == 0 ? "" : "-" + accessablesToString(parameters))); + } + + return new AccessResult(this, AccessResult.AccessStatus.GRANTED); + } + + public static String accessablesToString(Object[] accessObject) + { + StringBuilder sb = new StringBuilder(); + + for (Object access : accessObject) + { + sb.append(access.getClass().getSimpleName() + ":" + access.toString() + ", "); + } + + return sb.delete(sb.length() - 2, sb.length()).toString(); + } + + public String getPluginName() + { + return "AllowAll"; + } + + public void setConfiguaration(Configuration config) + { + //no-op + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.java new file mode 100644 index 0000000000..80c125e737 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/DenyAll.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.security.access.plugins; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.AccessResult; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.AMQConnectionException; +import org.apache.commons.configuration.Configuration; + +public class DenyAll implements ACLPlugin +{ + public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException + { + + if (ACLManager.getLogger().isInfoEnabled()) + { + } + ACLManager.getLogger().info("Denying user:" + session.getAuthorizedID() + " for :" + permission.toString() + + " on " + body.getClass().getSimpleName() + + (parameters == null || parameters.length == 0 ? "" : "-" + AllowAll.accessablesToString(parameters))); + + throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, "DenyAll Plugin"); + } + + public String getPluginName() + { + return "DenyAll"; + } + + public void setConfiguaration(Configuration config) + { + //no-op + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java new file mode 100644 index 0000000000..251f4e6330 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/plugins/SimpleXML.java @@ -0,0 +1,342 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.server.security.access.plugins; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicPublishBody; + +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.AccessResult; +import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.security.access.PrincipalPermissions; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This uses the default + */ +public class SimpleXML implements ACLPlugin +{ + private Map _users; + private final AccessResult GRANTED = new AccessResult(this, AccessResult.AccessStatus.GRANTED); + + public SimpleXML() + { + _users = new ConcurrentHashMap(); + } + + public void setConfiguaration(Configuration config) + { + processConfig(config); + } + + private void processConfig(Configuration config) + { + processPublish(config); + + processConsume(config); + + processCreate(config); + } + + /** + * Publish format takes + * Exchange + Routing Key Pairs + * + * @param config XML Configuration + */ + private void processPublish(Configuration config) + { + Configuration publishConfig = config.subset("security.access_control_list.publish"); + + //Process users that have full publish permission + String[] users = publishConfig.getStringArray("users.user"); + + for (String user : users) + { + grant(Permission.PUBLISH, user); + } + + // Process exchange limited users + int exchangeCount = 0; + Configuration exchangeConfig = publishConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + + while (!exchangeConfig.isEmpty()) + { + //Get Exchange Name + AMQShortString exchangeName = new AMQShortString(exchangeConfig.getString("name")); + + //Get Routing Keys + int keyCount = 0; + Configuration routingkeyConfig = exchangeConfig.subset("routing_keys.routing_key(" + keyCount + ")"); + + while (!routingkeyConfig.isEmpty()) + { + //Get RoutingKey Value + AMQShortString routingKeyValue = new AMQShortString(routingkeyConfig.getString("value")); + + //Apply Exchange + RoutingKey permissions to Users + users = routingkeyConfig.getStringArray("users.user"); + for (String user : users) + { + grant(Permission.PUBLISH, user, exchangeName, routingKeyValue); + } + + //Apply permissions to Groups + + // Check for more configs + keyCount++; + routingkeyConfig = exchangeConfig.subset("routing_keys.routing_key(" + keyCount + ")"); + } + + //Apply Exchange wide permissions to Users + users = exchangeConfig.getStringArray("exchange(" + exchangeCount + ").users.user"); + + for (String user : users) + { + grant(Permission.PUBLISH, user, exchangeName); + } + + //Apply permissions to Groups + exchangeCount++; + exchangeConfig = publishConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + } + } + + private void grant(Permission permission, String user, Object... parameters) + { + PrincipalPermissions permissions = _users.get(user); + + if (permissions == null) + { + permissions = new PrincipalPermissions(user); + } + + _users.put(user, permissions); + permissions.grant(permission, parameters); + } + + private void processConsume(Configuration config) + { + Configuration consumeConfig = config.subset("security.access_control_list.consume"); + + // Process queue limited users + int queueCount = 0; + Configuration queueConfig = consumeConfig.subset("queues.queue(" + queueCount + ")"); + + while (!queueConfig.isEmpty()) + { + //Get queue Name + AMQShortString queueName = new AMQShortString(queueConfig.getString("name")); + // if there is no name then there may be a temporary element + boolean temporary = queueConfig.containsKey("temporary"); + boolean ownQueues = queueConfig.containsKey("own_queues"); + + //Process permissions for this queue + String[] users = queueConfig.getStringArray("users.user"); + for (String user : users) + { + grant(Permission.CONSUME, user, queueName, temporary, ownQueues); + } + + //See if we have another config + queueCount++; + queueConfig = consumeConfig.subset("queues.queue(" + queueCount + ")"); + } + + // Process users that have full consume permission + String[] users = consumeConfig.getStringArray("users.user"); + + for (String user : users) + { + grant(Permission.CONSUME, user); + } + } + + private void processCreate(Configuration config) + { + Configuration createConfig = config.subset("security.access_control_list.create"); + + // Process create permissions for queue creation + int queueCount = 0; + Configuration queueConfig = createConfig.subset("queues.queue(" + queueCount + ")"); + + while (!queueConfig.isEmpty()) + { + //Get queue Name + AMQShortString queueName = new AMQShortString(queueConfig.getString("name")); + + // if there is no name then there may be a temporary element + boolean temporary = queueConfig.containsKey("temporary"); + + int exchangeCount = 0; + Configuration exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + + while (!exchangeConfig.isEmpty()) + { + + AMQShortString exchange = new AMQShortString(exchangeConfig.getString("name")); + AMQShortString routingKey = new AMQShortString(exchangeConfig.getString("routing_key")); + + //Process permissions for this queue + String[] users = exchangeConfig.getStringArray("users.user"); + for (String user : users) + { + grant(Permission.CREATE, user, temporary, + (queueName.equals("") ? null : queueName), + (exchange.equals("") ? null : exchange), + (routingKey.equals("") ? null : routingKey)); + } + + //See if we have another config + exchangeCount++; + exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + } + + // Process users that are not bound to an exchange + String[] users = queueConfig.getStringArray("users.user"); + + for (String user : users) + { + grant(Permission.CREATE, user, temporary, queueName); + } + + //See if we have another config + queueCount++; + queueConfig = createConfig.subset("queues.queue(" + queueCount + ")"); + } + + // Process create permissions for exchange creation + int exchangeCount = 0; + Configuration exchangeConfig = createConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + + while (!exchangeConfig.isEmpty()) + { + AMQShortString exchange = new AMQShortString(exchangeConfig.getString("name")); + AMQShortString clazz = new AMQShortString(exchangeConfig.getString("class")); + + //Process permissions for this queue + String[] users = exchangeConfig.getStringArray("users.user"); + for (String user : users) + { + grant(Permission.CREATE, user, exchange, clazz); + } + + //See if we have another config + exchangeCount++; + exchangeConfig = queueConfig.subset("exchanges.exchange(" + exchangeCount + ")"); + } + + // Process users that have full create permission + String[] users = createConfig.getStringArray("users.user"); + + for (String user : users) + { + grant(Permission.CREATE, user); + } + + + } + + public String getPluginName() + { + return "Simple"; + } + + public AccessResult authorise(AMQProtocolSession session, Permission permission, AMQMethodBody body, Object... parameters) throws AMQConnectionException + { + String error = ""; + + if (ACLManager.getLogger().isInfoEnabled()) + { + ACLManager.getLogger().info("Simple Authorisation processing user:" + session.getAuthorizedID() + " for :" + permission.toString() + + " on " + body.getClass().getSimpleName() + + (parameters == null || parameters.length == 0 ? "" : "-" + AllowAll.accessablesToString(parameters))); + } + + String username = session.getAuthorizedID().getName(); + + //Get the Users Permissions + PrincipalPermissions permissions = _users.get(username); + + if (permissions != null) + { + switch (permission) + { + case ACCESS: + return GRANTED; + case BIND: // Body QueueDeclareBody - Parameters : Exchange, Queue, QueueName + // Body QueueBindBody - Paramters : Exchange, Queue, QueueName + if (parameters.length == 3) + { + // Parameters : Exchange, Queue, RoutingKey + if (permissions.authorise(Permission.BIND, body, parameters[0], parameters[1], parameters[2])) + { + return GRANTED; + } + } + break; + case CONSUME: // Parameters : none + if (parameters.length == 1 && permissions.authorise(Permission.CONSUME, parameters[0])) + { + return GRANTED; + } + break; + case CREATE: // Body : QueueDeclareBody | ExchangeDeclareBody - Parameters : none + if (permissions.authorise(Permission.CREATE, body)) + { + return GRANTED; + } + break; + case PUBLISH: // Body : BasicPublishBody Parameters : exchange + if (parameters.length == 1 && parameters[0] instanceof Exchange) + { + if (permissions.authorise(Permission.PUBLISH, ((Exchange) parameters[0]).getName(), + ((BasicPublishBody) body).getRoutingKey())) + { + return GRANTED; + } + } + break; + case PURGE: + break; + case DELETE: + break; + case UNBIND: + break; + } + } + + //todo potential refactor this ConnectionException Out of here + throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, error); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java new file mode 100644 index 0000000000..3f846b9dd0 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth; + +import javax.security.sasl.SaslException; + +public class AuthenticationResult +{ + public enum AuthenticationStatus + { + SUCCESS, CONTINUE, ERROR + } + + public AuthenticationStatus status; + public byte[] challenge; + + private Exception cause; + + public AuthenticationResult(AuthenticationStatus status) + { + this(null, status, null); + } + + public AuthenticationResult(byte[] challenge, AuthenticationStatus status) + { + this(challenge, status, null); + } + + public AuthenticationResult(AuthenticationStatus error, Exception cause) + { + this(null, error, cause); + } + + public AuthenticationResult(byte[] challenge, AuthenticationStatus status, Exception cause) + { + this.status = status; + this.challenge = challenge; + this.cause = cause; + } + + public Exception getCause() + { + return cause; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java new file mode 100644 index 0000000000..cca9deb6da --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.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.security.auth.database; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.access.management.AMQUserManagementMBean; +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HashedInitialiser; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; +import java.security.Principal; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; +import java.util.regex.Pattern; + +/** + * Represents a user database where the account information is stored in a simple flat file. + * + * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn + * + * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text. + */ +public class Base64MD5PasswordFilePrincipalDatabase implements PrincipalDatabase +{ + private static final Logger _logger = Logger.getLogger(Base64MD5PasswordFilePrincipalDatabase.class); + + private File _passwordFile; + + private Pattern _regexp = Pattern.compile(":"); + + private Map _saslServers; + + AMQUserManagementMBean _mbean; + public static final String DEFAULT_ENCODING = "utf-8"; + private Map _users = new HashMap(); + private ReentrantLock _userUpdate = new ReentrantLock(); + + public Base64MD5PasswordFilePrincipalDatabase() + { + _saslServers = new HashMap(); + + /** + * Create Authenticators for MD5 Password file. + */ + + // Accept Plain incomming and hash it for comparison to the file. + CRAMMD5HashedInitialiser cram = new CRAMMD5HashedInitialiser(); + cram.initialise(this); + _saslServers.put(cram.getMechanismName(), cram); + + //fixme The PDs should setup a PD Mangement MBean +// try +// { +// _mbean = new AMQUserManagementMBean(); +// _mbean.setPrincipalDatabase(this); +// } +// catch (JMException e) +// { +// _logger.warn("User management disabled as unable to create MBean:" + e); +// } + } + + public void setPasswordFile(String passwordFile) throws IOException + { + File f = new File(passwordFile); + _logger.info("PasswordFilePrincipalDatabase using file " + f.getAbsolutePath()); + _passwordFile = f; + if (!f.exists()) + { + throw new FileNotFoundException("Cannot find password file " + f); + } + if (!f.canRead()) + { + throw new FileNotFoundException("Cannot read password file " + f + + ". Check permissions."); + } + + loadPasswordFile(); + } + + /** + * SASL Callback Mechanism - sets the Password in the PasswordCallback based on the value in the PasswordFile + * If you want to change the password for a user, use updatePassword instead. + * + * @param principal The Principal to set the password for + * @param callback The PasswordCallback to call setPassword on + * + * @throws AccountNotFoundException If the Principal cannont be found in this Database + */ + public void setPassword(Principal principal, PasswordCallback callback) throws AccountNotFoundException + { + if (_passwordFile == null) + { + throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation"); + } + if (principal == null) + { + throw new IllegalArgumentException("principal must not be null"); + } + + char[] pwd = lookupPassword(principal.getName()); + + if (pwd != null) + { + callback.setPassword(pwd); + } + else + { + throw new AccountNotFoundException("No account found for principal " + principal); + } + } + + /** + * Used to verify that the presented Password is correct. Currently only used by Management Console + * + * @param principal The principal to authenticate + * @param password The password to check + * + * @return true if password is correct + * + * @throws AccountNotFoundException if the principal cannot be found + */ + public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException + { + char[] pwd = lookupPassword(principal); + + return compareCharArray(pwd, password); + } + + private boolean compareCharArray(char[] a, char[] b) + { + boolean equal = false; + if (a.length == b.length) + { + equal = true; + int index = 0; + while (equal && index < a.length) + { + equal = a[index] == b[index]; + index++; + } + } + return equal; + } + + /** + * Changes the password for the specified user + * + * @param principal to change the password for + * @param password plaintext password to set the password too + */ + public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException + { + HashedUser user = _users.get(principal.getName()); + + if (user == null) + { + throw new AccountNotFoundException(principal.getName()); + } + + try + { + try + { + _userUpdate.lock(); + char[] orig = user.getPassword(); + user.setPassword(password); + + try + { + savePasswordFile(); + } + catch (IOException e) + { + _logger.error("Unable to save password file, password change for user'" + + principal + "' will revert at restart"); + //revert the password change + user.setPassword(orig); + return false; + } + return true; + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + catch (Exception e) + { + return false; + } + } + + public boolean createPrincipal(Principal principal, char[] password) + { + if (_users.get(principal.getName()) != null) + { + return false; + } + + HashedUser user = new HashedUser(principal.getName(), password); + + try + { + _userUpdate.lock(); + _users.put(user.getName(), user); + + try + { + savePasswordFile(); + return true; + } + catch (IOException e) + { + //remove the use on failure. + _users.remove(user.getName()); + return false; + } + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + + public boolean deletePrincipal(Principal principal) throws AccountNotFoundException + { + HashedUser user = _users.get(principal.getName()); + + if (user == null) + { + throw new AccountNotFoundException(principal.getName()); + } + + try + { + _userUpdate.lock(); + user.delete(); + + try + { + savePasswordFile(); + } + catch (IOException e) + { + _logger.warn("Unable to remove user '" + user.getName() + "' from password file."); + return false; + } + + _users.remove(user.getName()); + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + + return true; + } + + public Map getMechanisms() + { + return _saslServers; + } + + public List getUsers() + { + return new LinkedList(_users.values()); + } + + public Principal getUser(String username) + { + if (_users.containsKey(username)) + { + return new UsernamePrincipal(username); + } + return null; + } + + /** + * Looks up the password for a specified user in the password file. Note this code is not secure since it + * creates strings of passwords. It should be modified to create only char arrays which get nulled out. + * + * @param name The principal name to lookup + * + * @return a char[] for use in SASL. + */ + private char[] lookupPassword(String name) + { + HashedUser user = _users.get(name); + if (user == null) + { + return null; + } + else + { + return user.getPassword(); + } + } + + private void loadPasswordFile() throws IOException + { + try + { + _userUpdate.lock(); + _users.clear(); + + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(_passwordFile)); + String line; + + while ((line = reader.readLine()) != null) + { + String[] result = _regexp.split(line); + if (result == null || result.length < 2 || result[0].startsWith("#")) + { + continue; + } + + HashedUser user = new HashedUser(result); + _logger.info("Created user:" + user); + _users.put(user.getName(), user); + } + } + finally + { + if (reader != null) + { + reader.close(); + } + } + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + + private void savePasswordFile() throws IOException + { + try + { + _userUpdate.lock(); + + BufferedReader reader = null; + PrintStream writer = null; + File tmp = new File(_passwordFile.getAbsolutePath() + ".tmp"); + if (tmp.exists()) + { + tmp.delete(); + } + + try + { + writer = new PrintStream(tmp); + reader = new BufferedReader(new FileReader(_passwordFile)); + String line; + + while ((line = reader.readLine()) != null) + { + String[] result = _regexp.split(line); + if (result == null || result.length < 2 || result[0].startsWith("#")) + { + writer.write(line.getBytes(DEFAULT_ENCODING)); + writer.println(); + continue; + } + + HashedUser user = _users.get(result[0]); + + if (user == null) + { + writer.write(line.getBytes(DEFAULT_ENCODING)); + writer.println(); + } + else if (!user.isDeleted()) + { + if (!user.isModified()) + { + writer.write(line.getBytes(DEFAULT_ENCODING)); + writer.println(); + } + else + { + try + { + byte[] encodedPassword = user.getEncodedPassword(); + + writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING)); + writer.write(encodedPassword); + writer.println(); + + user.saved(); + } + catch (Exception e) + { + _logger.warn("Unable to encode new password reverting to old password."); + writer.write(line.getBytes(DEFAULT_ENCODING)); + writer.println(); + } + } + } + } + + for (HashedUser user : _users.values()) + { + if (user.isModified()) + { + byte[] encodedPassword; + try + { + encodedPassword = user.getEncodedPassword(); + writer.write((user.getName() + ":").getBytes(DEFAULT_ENCODING)); + writer.write(encodedPassword); + writer.println(); + user.saved(); + } + catch (Exception e) + { + _logger.warn("Unable to get Encoded password for user'" + user.getName() + "' password not saved"); + } + } + } + } + finally + { + if (reader != null) + { + reader.close(); + } + + if (writer != null) + { + writer.close(); + } + + // Swap temp file to main password file. + File old = new File(_passwordFile.getAbsoluteFile() + ".old"); + if (old.exists()) + { + old.delete(); + } + _passwordFile.renameTo(old); + tmp.renameTo(_passwordFile); + tmp.delete(); + } + } + finally + { + if (_userUpdate.isHeldByCurrentThread()) + { + _userUpdate.unlock(); + } + } + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java new file mode 100644 index 0000000000..15c62a62e4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java @@ -0,0 +1,235 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.database; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; + +import org.apache.log4j.Logger; + +import org.apache.qpid.configuration.PropertyUtils; +import org.apache.qpid.configuration.PropertyException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.access.management.AMQUserManagementMBean; +import org.apache.qpid.AMQException; + +import javax.management.JMException; + +public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatabaseManager +{ + private static final Logger _logger = Logger.getLogger(ConfigurationFilePrincipalDatabaseManager.class); + + private static final String _base = "security.principal-databases.principal-database"; + + Map _databases; + + public ConfigurationFilePrincipalDatabaseManager(Configuration configuration) throws Exception + { + _logger.info("Initialising PrincipleDatabase authentication manager"); + _databases = initialisePrincipalDatabases(configuration); + } + + private Map initialisePrincipalDatabases(Configuration configuration) throws Exception + { + List databaseNames = configuration.getList(_base + ".name"); + List databaseClasses = configuration.getList(_base + ".class"); + Map databases = new HashMap(); + + if (databaseNames.size() == 0) + { + _logger.warn("No Principal databases specified. Broker running with NO AUTHENTICATION"); + } + + for (int i = 0; i < databaseNames.size(); i++) + { + Object o; + try + { + o = Class.forName(databaseClasses.get(i)).newInstance(); + } + catch (Exception e) + { + throw new Exception("Error initialising principal database: " + e, e); + } + + if (!(o instanceof PrincipalDatabase)) + { + throw new Exception("Principal databases must implement the PrincipalDatabase interface"); + } + + initialisePrincipalDatabase((PrincipalDatabase) o, configuration, i); + + String name = databaseNames.get(i); + if ((name == null) || (name.length() == 0)) + { + throw new Exception("Principal database names must have length greater than or equal to one character"); + } + + PrincipalDatabase pd = databases.get(name); + if (pd != null) + { + throw new Exception("Duplicate principal database name not provided"); + } + + _logger.info("Initialised principal database '" + name + "' successfully"); + databases.put(name, (PrincipalDatabase) o); + } + + return databases; + } + + private void initialisePrincipalDatabase(PrincipalDatabase principalDatabase, Configuration config, int index) + throws FileNotFoundException, ConfigurationException + { + String baseName = _base + "(" + index + ").attributes.attribute."; + List argumentNames = config.getList(baseName + "name"); + List argumentValues = config.getList(baseName + "value"); + for (int i = 0; i < argumentNames.size(); i++) + { + String argName = argumentNames.get(i); + if ((argName == null) || (argName.length() == 0)) + { + throw new ConfigurationException("Argument names must have length >= 1 character"); + } + + if (Character.isLowerCase(argName.charAt(0))) + { + argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1); + } + + String methodName = "set" + argName; + Method method = null; + try + { + method = principalDatabase.getClass().getMethod(methodName, String.class); + } + catch (Exception e) + { + // do nothing.. as on error method will be null + } + + if (method == null) + { + throw new ConfigurationException("No method " + methodName + " found in class " + + principalDatabase.getClass() + + " hence unable to configure principal database. The method must be public and " + + "have a single String argument with a void return type"); + } + + try + { + method.invoke(principalDatabase, PropertyUtils.replaceProperties(argumentValues.get(i))); + } + catch (Exception ite) + { + if (ite instanceof ConfigurationException) + { + throw(ConfigurationException) ite; + } + else + { + throw new ConfigurationException(ite.getMessage(), ite); + } + } + } + } + + public Map getDatabases() + { + return _databases; + } + + public void initialiseManagement(Configuration config) throws ConfigurationException + { + try + { + AMQUserManagementMBean _mbean = new AMQUserManagementMBean(); + + String baseSecurity = "security.jmx"; + List principalDBs = config.getList(baseSecurity + ".principal-database"); + + if (principalDBs.size() == 0) + { + throw new ConfigurationException("No principal-database specified for jmx security(" + baseSecurity + ".principal-database)"); + } + + String databaseName = principalDBs.get(0); + + PrincipalDatabase database = getDatabases().get(databaseName); + + if (database == null) + { + throw new ConfigurationException("Principal-database '" + databaseName + "' not found"); + } + + _mbean.setPrincipalDatabase(database); + + List jmxaccesslist = config.getList(baseSecurity + ".access"); + + if (jmxaccesslist.size() == 0) + { + throw new ConfigurationException("No access control files specified for jmx security(" + baseSecurity + ".access)"); + } + + String jmxaccesssFile = null; + + try + { + jmxaccesssFile = PropertyUtils.replaceProperties(jmxaccesslist.get(0)); + } + catch (PropertyException e) + { + throw new ConfigurationException("Unable to parse access control filename '" + jmxaccesssFile + "'"); + } + + try + { + _mbean.setAccessFile(jmxaccesssFile); + } + catch (IOException e) + { + _logger.warn("Unable to load access file:" + jmxaccesssFile); + } + + try + { + _mbean.register(); + } + catch (AMQException e) + { + _logger.warn("Unable to register user management MBean"); + } + } + catch (JMException e) + { + _logger.warn("User management disabled as unable to create MBean:" + e); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/HashedUser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/HashedUser.java new file mode 100644 index 0000000000..4d92e3fb4c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/HashedUser.java @@ -0,0 +1,134 @@ +/* +* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.database; + +import org.apache.commons.codec.EncoderException; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; + +import java.io.UnsupportedEncodingException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; + +public class HashedUser implements Principal +{ + private static final Logger _logger = Logger.getLogger(HashedUser.class); + + String _name; + char[] _password; + byte[] _encodedPassword = null; + private boolean _modified = false; + private boolean _deleted = false; + + HashedUser(String[] data) throws UnsupportedEncodingException + { + if (data.length != 2) + { + throw new IllegalArgumentException("User Data should be length 2, username, password"); + } + + _name = data[0]; + + byte[] encoded_password = data[1].getBytes(Base64MD5PasswordFilePrincipalDatabase.DEFAULT_ENCODING); + + Base64 b64 = new Base64(); + byte[] decoded = b64.decode(encoded_password); + + _encodedPassword = encoded_password; + + _password = new char[decoded.length]; + + int index = 0; + for (byte c : decoded) + { + _password[index++] = (char) c; + } + } + + public HashedUser(String name, char[] password) + { + _name = name; + setPassword(password); + } + + public String getName() + { + return _name; + } + + public String toString() + { + return _name; + } + + char[] getPassword() + { + return _password; + } + + void setPassword(char[] password) + { + _password = password; + _modified = true; + _encodedPassword = null; + } + + byte[] getEncodedPassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException + { + if (_encodedPassword == null) + { + encodePassword(); + } + return _encodedPassword; + } + + private void encodePassword() throws EncoderException, UnsupportedEncodingException, NoSuchAlgorithmException + { + byte[] byteArray = new byte[_password.length]; + int index = 0; + for (char c : _password) + { + byteArray[index++] = (byte) c; + } + _encodedPassword = (new Base64()).encode(byteArray); + } + + public boolean isModified() + { + return _modified; + } + + public boolean isDeleted() + { + return _deleted; + } + + public void delete() + { + _deleted = true; + } + + public void saved() + { + _modified = false; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java new file mode 100644 index 0000000000..352d41a0ba --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.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.security.auth.database; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.amqplain.AmqPlainInitialiser; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser; +import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.security.Principal; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Represents a user database where the account information is stored in a simple flat file. + * + * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn + * + * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text. + */ +public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase +{ + private static final Logger _logger = Logger.getLogger(PlainPasswordFilePrincipalDatabase.class); + + protected File _passwordFile; + + protected Pattern _regexp = Pattern.compile(":"); + + protected Map _saslServers; + + public PlainPasswordFilePrincipalDatabase() + { + _saslServers = new HashMap(); + + /** + * Create Authenticators for Plain Password file. + */ + + // Accept AMQPlain incomming and compare it to the file. + AmqPlainInitialiser amqplain = new AmqPlainInitialiser(); + amqplain.initialise(this); + + // Accept Plain incomming and compare it to the file. + PlainInitialiser plain = new PlainInitialiser(); + plain.initialise(this); + + // Accept MD5 incomming and Hash file value for comparison + CRAMMD5Initialiser cram = new CRAMMD5Initialiser(); + cram.initialise(this); + + _saslServers.put(amqplain.getMechanismName(), amqplain); + _saslServers.put(plain.getMechanismName(), plain); + _saslServers.put(cram.getMechanismName(), cram); + } + + public void setPasswordFile(String passwordFile) throws FileNotFoundException + { + File f = new File(passwordFile); + _logger.info("PlainPasswordFile using file " + f.getAbsolutePath()); + _passwordFile = f; + if (!f.exists()) + { + throw new FileNotFoundException("Cannot find password file " + f); + } + if (!f.canRead()) + { + throw new FileNotFoundException("Cannot read password file " + f + + ". Check permissions."); + } + } + + public void setPassword(Principal principal, PasswordCallback callback) throws IOException, + AccountNotFoundException + { + if (_passwordFile == null) + { + throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation"); + } + if (principal == null) + { + throw new IllegalArgumentException("principal must not be null"); + } + char[] pwd = lookupPassword(principal.getName()); + if (pwd != null) + { + callback.setPassword(pwd); + } + else + { + throw new AccountNotFoundException("No account found for principal " + principal); + } + } + + public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException + { + try + { + char[] pwd = lookupPassword(principal); + + return compareCharArray(pwd, password); + } + catch (IOException e) + { + return false; + } + } + + public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException + { + return false; // updates denied + } + + public boolean createPrincipal(Principal principal, char[] password) + { + return false; // updates denied + } + + public boolean deletePrincipal(Principal principal) throws AccountNotFoundException + { + return false; // updates denied + } + + public Map getMechanisms() + { + return _saslServers; + } + + public List getUsers() + { + return new LinkedList(); //todo + } + + public Principal getUser(String username) + { + try + { + if (lookupPassword(username) != null) + { + return new UsernamePrincipal(username); + } + } + catch (IOException e) + { + //fall through to null return + } + return null; + } + + private boolean compareCharArray(char[] a, char[] b) + { + boolean equal = false; + if (a.length == b.length) + { + equal = true; + int index = 0; + while (equal && index < a.length) + { + equal = a[index] == b[index]; + index++; + } + } + return equal; + } + + + /** + * Looks up the password for a specified user in the password file. Note this code is not secure since it + * creates strings of passwords. It should be modified to create only char arrays which get nulled out. + * + * @param name the name of the principal to lookup + * + * @return char[] of the password + * + * @throws java.io.IOException whilst accessing the file + */ + private char[] lookupPassword(String name) throws IOException + { + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(_passwordFile)); + String line; + + while ((line = reader.readLine()) != null) + { + if (!line.startsWith("#")) + { + String[] result = _regexp.split(line); + if (result == null || result.length < 2) + { + continue; + } + + if (name.equals(result[0])) + { + return result[1].toCharArray(); + } + } + } + return null; + } + finally + { + if (reader != null) + { + reader.close(); + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java new file mode 100644 index 0000000000..a82f9ed40b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java @@ -0,0 +1,100 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.database; + +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Map; +import java.util.List; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; + +/** Represents a "user database" which is really a way of storing principals (i.e. usernames) and passwords. */ +public interface PrincipalDatabase +{ + /** + * Set the password for a given principal in the specified callback. This is used for certain SASL providers. The + * user database implementation should look up the password in any way it chooses and set it in the callback by + * calling its setPassword method. + * + * @param principal the principal + * @param callback the password callback that wants to receive the password + * + * @throws AccountNotFoundException if the account for specified principal could not be found + * @throws IOException if there was an error looking up the principal + */ + void setPassword(Principal principal, PasswordCallback callback) + throws IOException, AccountNotFoundException; + + /** + * Used to verify that the presented Password is correct. Currently only used by Management Console + * @param principal The principal to authenticate + * @param password The password to check + * @return true if password is correct + * @throws AccountNotFoundException if the principal cannot be found + */ + boolean verifyPassword(String principal, char[] password) + throws AccountNotFoundException; + + /** + * Update(Change) the password for the given principal + * @param principal Who's password is to be changed + * @param password The new password to use + * @return True if change was successful + * @throws AccountNotFoundException If the given principal doesn't exist in the Database + */ + boolean updatePassword(Principal principal, char[] password) + throws AccountNotFoundException; + + /** + * Create a new principal in the database + * @param principal The principal to create + * @param password The password to set for the principal + * @return True on a successful creation + */ + boolean createPrincipal(Principal principal, char[] password); + + /** + * Delete a principal + * @param principal The principal to delete + * @return True on a successful creation + * @throws AccountNotFoundException If the given principal doesn't exist in the Database + */ + boolean deletePrincipal(Principal principal) + throws AccountNotFoundException; + + /** + * Get the principal from the database with the given username + * @param username of the principal to lookup + * @return The Principal object for the given username or null if not found. + */ + Principal getUser(String username); + + + public Map getMechanisms(); + + + List getUsers(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java new file mode 100644 index 0000000000..2c553ae76a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.auth.database; + +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; + +import java.util.Map; + +public interface PrincipalDatabaseManager +{ + public Map getDatabases(); + + public void initialiseManagement(Configuration config) throws ConfigurationException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java new file mode 100644 index 0000000000..c8a4add0f1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.auth.database; + +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser; +import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; +import java.util.Properties; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.LinkedList; +import java.security.Principal; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +public class PropertiesPrincipalDatabase implements PrincipalDatabase +{ + private Properties _users; + + private Map _saslServers; + + public PropertiesPrincipalDatabase(Properties users) + { + _users = users; + + _saslServers = new HashMap(); + + /** + * Create Authenticators for Properties Principal Database. + */ + + // Accept MD5 incomming and use plain comparison with the file + PlainInitialiser cram = new PlainInitialiser(); + cram.initialise(this); + // Accept Plain incomming and hash it for comparison to the file. + CRAMMD5Initialiser plain = new CRAMMD5Initialiser(); + plain.initialise(this, CRAMMD5Initialiser.HashDirection.INCOMMING); + + _saslServers.put(plain.getMechanismName(), cram); + _saslServers.put(cram.getMechanismName(), plain); + } + + public void setPassword(Principal principal, PasswordCallback callback) throws IOException, AccountNotFoundException + { + if (principal == null) + { + throw new IllegalArgumentException("principal must not be null"); + } + + + + final String pwd = _users.getProperty(principal.getName()); + + if (pwd != null) + { + callback.setPassword(pwd.toCharArray()); + } + else + { + throw new AccountNotFoundException("No account found for principal " + principal); + } + } + + public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException + { + //fixme this is not correct as toCharArray is not safe based on the type of string. + char[] pwd = _users.getProperty(principal).toCharArray(); + + return compareCharArray(pwd, password); + } + + public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException + { + return false; // updates denied + } + + public boolean createPrincipal(Principal principal, char[] password) + { + return false; // updates denied + } + + public boolean deletePrincipal(Principal principal) throws AccountNotFoundException + { + return false; // updates denied + } + + private boolean compareCharArray(char[] a, char[] b) + { + boolean equal = false; + if (a.length == b.length) + { + equal = true; + int index = 0; + while (equal && index < a.length) + { + equal = a[index] == b[index]; + index++; + } + } + return equal; + } + + private char[] convertPassword(String password) throws UnsupportedEncodingException + { + byte[] passwdBytes = password.getBytes("utf-8"); + + char[] passwd = new char[passwdBytes.length]; + + int index = 0; + + for (byte b : passwdBytes) + { + passwd[index++] = (char) b; + } + + return passwd; + } + + + public Map getMechanisms() + { + return _saslServers; + } + + public List getUsers() + { + return new LinkedList(); //todo + } + + public Principal getUser(String username) + { + if (_users.getProperty(username) != null) + { + return new UsernamePrincipal(username); + } + else + { + return null; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java new file mode 100644 index 0000000000..6b86a46bd2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.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.security.auth.database; + +import org.apache.commons.configuration.Configuration; + +import java.util.Map; +import java.util.Properties; +import java.util.HashMap; + +public class PropertiesPrincipalDatabaseManager implements PrincipalDatabaseManager +{ + + Map _databases = new HashMap(); + + public PropertiesPrincipalDatabaseManager(String name, Properties users) + { + _databases.put(name, new PropertiesPrincipalDatabase(users)); + } + + public Map getDatabases() + { + return _databases; + } + + public void initialiseManagement(Configuration config) + { + //todo + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java new file mode 100644 index 0000000000..d1803124a7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.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.security.auth.manager; + +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.security.auth.AuthenticationResult; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +public interface AuthenticationManager +{ + String getMechanisms(); + + SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException; + + AuthenticationResult authenticate(SaslServer server, byte[] response); + + void close(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java new file mode 100644 index 0000000000..2cbbdc85ff --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.auth.manager; + +import org.apache.log4j.Logger; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.JCAProvider; +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; +import org.apache.qpid.server.security.auth.AuthenticationResult; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.SaslServerFactory; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslException; +import javax.security.sasl.Sasl; +import java.util.Map; +import java.util.HashMap; +import java.util.TreeMap; +import java.security.Security; + +public class PrincipalDatabaseAuthenticationManager implements AuthenticationManager +{ + private static final Logger _logger = Logger.getLogger(PrincipalDatabaseAuthenticationManager.class); + + /** The list of mechanisms, in the order in which they are configured (i.e. preferred order) */ + private String _mechanisms; + + /** Maps from the mechanism to the callback handler to use for handling those requests */ + private Map _callbackHandlerMap = new HashMap(); + + /** + * Maps from the mechanism to the properties used to initialise the server. See the method Sasl.createSaslServer for + * details of the use of these properties. This map is populated during initialisation of each provider. + */ + private Map> _serverCreationProperties = new HashMap>(); + + private AuthenticationManager _default = null; + /** The name for the required SASL Server mechanisms */ + public static final String PROVIDER_NAME= "AMQSASLProvider-Server"; + + public PrincipalDatabaseAuthenticationManager(String name, Configuration hostConfig) throws Exception + { + _logger.info("Initialising " + (name == null ? "Default" : "'" + name + "'") + + " PrincipleDatabase authentication manager."); + + // Fixme This should be done per Vhost but allowing global hack isn't right but ... + // required as authentication is done before Vhost selection + + Map> providerMap = new TreeMap>(); + + + if (name == null || hostConfig == null) + { + initialiseAuthenticationMechanisms(providerMap, ApplicationRegistry.getInstance().getDatabaseManager().getDatabases()); + } + else + { + String databaseName = hostConfig.getString("security.authentication.name"); + + if (databaseName == null) + { + + _default = ApplicationRegistry.getInstance().getAuthenticationManager(); + return; + } + else + { + PrincipalDatabase database = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().get(databaseName); + + if (database == null) + { + throw new ConfigurationException("Requested database:" + databaseName + " was not found"); + } + + initialiseAuthenticationMechanisms(providerMap, database); + } + } + + if (providerMap.size() > 0) + { + // Ensure we are used before the defaults + if (Security.insertProviderAt(new JCAProvider(PROVIDER_NAME, providerMap), 1) == -1) + { + _logger.error("Unable to load custom SASL providers. Qpid custom SASL authenticators unavailable."); + } + else + { + _logger.info("Additional SASL providers successfully registered."); + } + + } + else + { + _logger.warn("No additional SASL providers registered."); + } + + } + + + private void initialiseAuthenticationMechanisms(Map> providerMap, Map databases) throws Exception + { +// Configuration config = ApplicationRegistry.getInstance().getConfiguration(); +// List mechanisms = config.getList("security.sasl.mechanisms.mechanism.initialiser.class"); +// +// // Maps from the mechanism to the properties used to initialise the server. See the method +// // Sasl.createSaslServer for details of the use of these properties. This map is populated during initialisation +// // of each provider. + + + if (databases.size() > 1) + { + _logger.warn("More than one principle database provided currently authentication mechanism will override each other."); + } + + for (Map.Entry entry : databases.entrySet()) + { + + // fixme As the database now provide the mechanisms they support, they will ... + // overwrite each other in the map. There should only be one database per vhost. + // But currently we must have authentication before vhost definition. + initialiseAuthenticationMechanisms(providerMap, entry.getValue()); + } + + } + + private void initialiseAuthenticationMechanisms(Map> providerMap, PrincipalDatabase database) throws Exception + { + if (database == null || database.getMechanisms().size() == 0) + { + _logger.warn("No Database or no mechanisms to initialise authentication"); + return; + } + + for (Map.Entry mechanism : database.getMechanisms().entrySet()) + { + initialiseAuthenticationMechanism(mechanism.getKey(), mechanism.getValue(), providerMap); + } + } + + private void initialiseAuthenticationMechanism(String mechanism, AuthenticationProviderInitialiser initialiser, + Map> providerMap) + throws Exception + { + if (_mechanisms == null) + { + _mechanisms = mechanism; + } + else + { + // simple append should be fine since the number of mechanisms is small and this is a one time initialisation + _mechanisms = _mechanisms + " " + mechanism; + } + _callbackHandlerMap.put(mechanism, initialiser.getCallbackHandler()); + _serverCreationProperties.put(mechanism, initialiser.getProperties()); + Class factory = initialiser.getServerFactoryClassForJCARegistration(); + if (factory != null) + { + providerMap.put(mechanism, factory); + } + _logger.info("Initialised " + mechanism + " SASL provider successfully"); + } + + public String getMechanisms() + { + if (_default != null) + { + // Use the default AuthenticationManager if present + return _default.getMechanisms(); + } + else + { + return _mechanisms; + } + } + + public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException + { + if (_default != null) + { + // Use the default AuthenticationManager if present + return _default.createSaslServer(mechanism, localFQDN); + } + else + { + return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, _serverCreationProperties.get(mechanism), + _callbackHandlerMap.get(mechanism)); + } + + } + + public AuthenticationResult authenticate(SaslServer server, byte[] response) + { + // Use the default AuthenticationManager if present + if (_default != null) + { + return _default.authenticate(server, response); + } + + + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete()) + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.SUCCESS); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + public void close() + { + Security.removeProvider(PROVIDER_NAME); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java new file mode 100644 index 0000000000..89e545d6f5 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.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.security.auth.sasl; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.SaslServerFactory; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; + +public interface AuthenticationProviderInitialiser +{ + /** + * @return the mechanism's name. This will be used in the list of mechanism's advertised to the + * client. + */ + String getMechanismName(); + + /** + * Initialise the authentication provider. + * @param baseConfigPath the path in the config file that points to any config options for this provider. Each + * provider can have its own set of configuration options + * @param configuration the Apache Commons Configuration instance used to configure this provider + * @param principalDatabases the set of principal databases that are available + * @throws Exception needs refined Exception is too broad. + */ + void initialise(String baseConfigPath, Configuration configuration, + Map principalDatabases) throws Exception; + + /** + * Initialise the authentication provider. + * @param db The principal database to initialise with + */ + void initialise(PrincipalDatabase db); + + + /** + * @return the callback handler that should be used to process authentication requests for this mechanism. This will + * be called after initialise and will be stored by the authentication manager. The callback handler must be + * fully threadsafe. + */ + CallbackHandler getCallbackHandler(); + + /** + * Get the properties that must be passed in to the Sasl.createSaslServer method. + * @return the properties, which may be null + */ + Map getProperties(); + + /** + * Get the class that is the server factory. This is used for the JCA registration. + * @return null if no JCA registration is required, otherwise return the class + * that will be used in JCA registration + */ + Class getServerFactoryClassForJCARegistration(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java new file mode 100644 index 0000000000..d6a09d8217 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.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.security.auth.sasl; + +import java.security.Provider; +import java.security.Security; +import java.util.Map; + +import javax.security.sasl.SaslServerFactory; + +public final class JCAProvider extends Provider +{ + public JCAProvider(String name, Map> providerMap) + { + super(name, 1.0, "A JCA provider that registers all " + + "AMQ SASL providers that want to be registered"); + register(providerMap); + } + + private void register(Map> providerMap) + { + for (Map.Entry> me : + providerMap.entrySet()) + { + put("SaslServerFactory." + me.getKey(), me.getValue().getName()); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java new file mode 100644 index 0000000000..dd0bd096c3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java @@ -0,0 +1,123 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl; + +import java.io.IOException; +import java.security.Principal; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.AccountNotFoundException; +import javax.security.sasl.AuthorizeCallback; + +import org.apache.commons.configuration.Configuration; + +import org.apache.log4j.Logger; + +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; + +public abstract class UsernamePasswordInitialiser implements AuthenticationProviderInitialiser +{ + protected static final Logger _logger = Logger.getLogger(UsernamePasswordInitialiser.class); + + private ServerCallbackHandler _callbackHandler; + + private class ServerCallbackHandler implements CallbackHandler + { + private final PrincipalDatabase _principalDatabase; + + protected ServerCallbackHandler(PrincipalDatabase database) + { + _principalDatabase = database; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + Principal username = null; + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + username = new UsernamePrincipal(((NameCallback) callback).getDefaultName()); + } + else if (callback instanceof PasswordCallback) + { + try + { + _principalDatabase.setPassword(username, (PasswordCallback) callback); + } + catch (AccountNotFoundException e) + { + // very annoyingly the callback handler does not throw anything more appropriate than + // IOException + IOException ioe = new IOException("Error looking up user " + e); + ioe.initCause(e); + throw ioe; + } + } + else if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } + + public void initialise(String baseConfigPath, Configuration configuration, + Map principalDatabases) throws Exception + { + String principalDatabaseName = configuration.getString(baseConfigPath + ".principal-database"); + PrincipalDatabase db = principalDatabases.get(principalDatabaseName); + + initialise(db); + } + + public void initialise(PrincipalDatabase db) + { + if (db == null) + { + throw new NullPointerException("Cannot initialise with a null Principal database."); + } + + _callbackHandler = new ServerCallbackHandler(db); + } + + public CallbackHandler getCallbackHandler() + { + return _callbackHandler; + } + + public Map getProperties() + { + // there are no properties required for the CRAM-MD5 implementation + return null; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java new file mode 100644 index 0000000000..d7c8383690 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl; + +import java.security.Principal; + +/** A principal that is just a wrapper for a simple username. */ +public class UsernamePrincipal implements Principal +{ + private String _name; + + public UsernamePrincipal(String name) + { + _name = name; + } + + public String getName() + { + return _name; + } + + public String toString() + { + return _name; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java new file mode 100644 index 0000000000..7acc6322d1 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.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.security.auth.sasl.amqplain; + +import javax.security.sasl.SaslServerFactory; + +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; + +public class AmqPlainInitialiser extends UsernamePasswordInitialiser +{ + public String getMechanismName() + { + return "AMQPLAIN"; + } + + public Class getServerFactoryClassForJCARegistration() + { + return AmqPlainSaslServerFactory.class; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java new file mode 100644 index 0000000000..9f56b8521a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java @@ -0,0 +1,132 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.amqplain; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; + +public class AmqPlainSaslServer implements SaslServer +{ + public static final String MECHANISM = "AMQPLAIN"; + + private CallbackHandler _cbh; + + private String _authorizationId; + + private boolean _complete = false; + + public AmqPlainSaslServer(CallbackHandler cbh) + { + _cbh = cbh; + } + + public String getMechanismName() + { + return MECHANISM; + } + + public byte[] evaluateResponse(byte[] response) throws SaslException + { + try + { + final FieldTable ft = FieldTableFactory.newFieldTable(ByteBuffer.wrap(response), response.length); + String username = (String) ft.getString("LOGIN"); + // we do not care about the prompt but it throws if null + NameCallback nameCb = new NameCallback("prompt", username); + // we do not care about the prompt but it throws if null + PasswordCallback passwordCb = new PasswordCallback("prompt", false); + // TODO: should not get pwd as a String but as a char array... + String pwd = (String) ft.getString("PASSWORD"); + AuthorizeCallback authzCb = new AuthorizeCallback(username, username); + Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; + _cbh.handle(callbacks); + String storedPwd = new String(passwordCb.getPassword()); + if (storedPwd.equals(pwd)) + { + _complete = true; + } + if (authzCb.isAuthorized() && _complete) + { + _authorizationId = authzCb.getAuthenticationID(); + return null; + } + else + { + throw new SaslException("Authentication failed"); + } + } + catch (AMQFrameDecodingException e) + { + throw new SaslException("Unable to decode response: " + e, e); + } + catch (IOException e) + { + throw new SaslException("Error processing data: " + e, e); + } + catch (UnsupportedCallbackException e) + { + throw new SaslException("Unable to obtain data from callback handler: " + e, e); + } + } + + public boolean isComplete() + { + return _complete; + } + + public String getAuthorizationID() + { + return _authorizationId; + } + + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("Unsupported operation"); + } + + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("Unsupported operation"); + } + + public Object getNegotiatedProperty(String propName) + { + return null; + } + + public void dispose() throws SaslException + { + _cbh = null; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java new file mode 100644 index 0000000000..67d20136bf --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.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.security.auth.sasl.amqplain; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + +public class AmqPlainSaslServerFactory implements SaslServerFactory +{ + public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + if (AmqPlainSaslServer.MECHANISM.equals(mechanism)) + { + return new AmqPlainSaslServer(cbh); + } + else + { + return null; + } + } + + public String[] getMechanismNames(Map props) + { + if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE)) + { + // returned array must be non null according to interface documentation + return new String[0]; + } + else + { + return new String[]{AmqPlainSaslServer.MECHANISM}; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java new file mode 100644 index 0000000000..97f9a4e91a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.crammd5; + +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; + +import javax.security.sasl.SaslServerFactory; +import java.util.Map; + +public class CRAMMD5HashedInitialiser extends UsernamePasswordInitialiser +{ + public String getMechanismName() + { + return CRAMMD5HashedSaslServer.MECHANISM; + } + + public Class getServerFactoryClassForJCARegistration() + { + return CRAMMD5HashedServerFactory.class; + } + + public void initialise(PrincipalDatabase passwordFile) + { + super.initialise(passwordFile); + } + + public Map getProperties() + { + return null; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java new file mode 100644 index 0000000000..f6cab084ea --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.auth.sasl.crammd5; + +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslException; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslServerFactory; +import javax.security.auth.callback.CallbackHandler; +import java.util.Enumeration; +import java.util.Map; + +public class CRAMMD5HashedSaslServer implements SaslServer +{ + public static final String MECHANISM = "CRAM-MD5-HASHED"; + + private SaslServer _realServer; + + public CRAMMD5HashedSaslServer(String mechanism, String protocol, String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + Enumeration factories = Sasl.getSaslServerFactories(); + + while (factories.hasMoreElements()) + { + SaslServerFactory factory = (SaslServerFactory) factories.nextElement(); + + if (factory instanceof CRAMMD5HashedServerFactory) + { + continue; + } + + String[] mechs = factory.getMechanismNames(props); + + for (String mech : mechs) + { + if (mech.equals("CRAM-MD5")) + { + _realServer = factory.createSaslServer("CRAM-MD5", protocol, serverName, props, cbh); + return; + } + } + } + + throw new RuntimeException("No default SaslServer found for mechanism:" + "CRAM-MD5"); + } + + public String getMechanismName() + { + return MECHANISM; + } + + public byte[] evaluateResponse(byte[] response) throws SaslException + { + return _realServer.evaluateResponse(response); + } + + public boolean isComplete() + { + return _realServer.isComplete(); + } + + public String getAuthorizationID() + { + return _realServer.getAuthorizationID(); + } + + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + return _realServer.unwrap(incoming, offset, len); + } + + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + return _realServer.wrap(outgoing, offset, len); + } + + public Object getNegotiatedProperty(String propName) + { + return _realServer.getNegotiatedProperty(propName); + } + + public void dispose() throws SaslException + { + _realServer.dispose(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java new file mode 100644 index 0000000000..5298b5cc63 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.crammd5; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + +public class CRAMMD5HashedServerFactory implements SaslServerFactory +{ + public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + if (mechanism.equals(CRAMMD5HashedSaslServer.MECHANISM)) + { + return new CRAMMD5HashedSaslServer(mechanism, protocol, serverName, props, cbh); + } + else + { + return null; + } + } + + public String[] getMechanismNames(Map props) + { + if (props != null) + { + if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE)) + { + // returned array must be non null according to interface documentation + return new String[0]; + } + } + + return new String[]{CRAMMD5HashedSaslServer.MECHANISM}; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java new file mode 100644 index 0000000000..264832888d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.crammd5; + +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; + +import javax.security.sasl.SaslServerFactory; + +public class CRAMMD5Initialiser extends UsernamePasswordInitialiser +{ + private HashDirection _hashDirection; + + public enum HashDirection + { + INCOMMING, PASSWORD_FILE + } + + + public String getMechanismName() + { + return "CRAM-MD5"; + } + + public Class getServerFactoryClassForJCARegistration() + { + // since the CRAM-MD5 provider is registered as part of the JDK, we do not + // return the factory class here since we do not need to register it ourselves. + if (_hashDirection == HashDirection.PASSWORD_FILE) + { + return null; + } + else + { + //fixme we need a server that will correctly has the incomming plain text for comparison to file. + _logger.warn("we need a server that will correctly convert the incomming plain text for comparison to file."); + return null; + } + } + + public void initialise(PrincipalDatabase passwordFile) + { + initialise(passwordFile, HashDirection.PASSWORD_FILE); + } + + public void initialise(PrincipalDatabase passwordFile, HashDirection direction) + { + super.initialise(passwordFile); + + _hashDirection = direction; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java new file mode 100644 index 0000000000..1d16cd8755 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.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.security.auth.sasl.plain; + +import javax.security.sasl.SaslServerFactory; + +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; + +public class PlainInitialiser extends UsernamePasswordInitialiser +{ + public String getMechanismName() + { + return "PLAIN"; + } + + public Class getServerFactoryClassForJCARegistration() + { + return PlainSaslServerFactory.class; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java new file mode 100644 index 0000000000..45fb9a4e42 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.plain; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +public class PlainSaslServer implements SaslServer +{ + public static final String MECHANISM = "PLAIN"; + + private CallbackHandler _cbh; + + private String _authorizationId; + + private boolean _complete = false; + + public PlainSaslServer(CallbackHandler cbh) + { + _cbh = cbh; + } + + public String getMechanismName() + { + return MECHANISM; + } + + public byte[] evaluateResponse(byte[] response) throws SaslException + { + try + { + int authzidNullPosition = findNullPosition(response, 0); + if (authzidNullPosition < 0) + { + throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found"); + } + int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); + if (authcidNullPosition < 0) + { + throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found"); + } + + // we do not currently support authcid in any meaningful way + // String authcid = new String(response, 0, authzidNullPosition, "utf8"); + String authzid = new String(response, authzidNullPosition + 1, authcidNullPosition - 1, "utf8"); + + // we do not care about the prompt but it throws if null + NameCallback nameCb = new NameCallback("prompt", authzid); + PasswordCallback passwordCb = new PasswordCallback("prompt", false); + // TODO: should not get pwd as a String but as a char array... + int passwordLen = response.length - authcidNullPosition - 1; + String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8"); + AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid); + Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; + _cbh.handle(callbacks); + String storedPwd = new String(passwordCb.getPassword()); + if (storedPwd.equals(pwd)) + { + _complete = true; + } + if (authzCb.isAuthorized() && _complete) + { + _authorizationId = authzCb.getAuthenticationID(); + return null; + } + else + { + throw new SaslException("Authentication failed"); + } + } + catch (IOException e) + { + throw new SaslException("Error processing data: " + e, e); + } + catch (UnsupportedCallbackException e) + { + throw new SaslException("Unable to obtain data from callback handler: " + e, e); + } + } + + private int findNullPosition(byte[] response, int startPosition) + { + int position = startPosition; + while (position < response.length) + { + if (response[position] == (byte) 0) + { + return position; + } + position++; + } + return -1; + } + + public boolean isComplete() + { + return _complete; + } + + public String getAuthorizationID() + { + return _authorizationId; + } + + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("Unsupported operation"); + } + + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("Unsupported operation"); + } + + public Object getNegotiatedProperty(String propName) + { + return null; + } + + public void dispose() throws SaslException + { + _cbh = null; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java new file mode 100644 index 0000000000..f0dd9eeb6d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.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.security.auth.sasl.plain; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + +public class PlainSaslServerFactory implements SaslServerFactory +{ + public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props, + CallbackHandler cbh) throws SaslException + { + if (PlainSaslServer.MECHANISM.equals(mechanism)) + { + return new PlainSaslServer(cbh); + } + else + { + return null; + } + } + + public String[] getMechanismNames(Map props) + { + if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE)) + { + // returned array must be non null according to interface documentation + return new String[0]; + } + else + { + return new String[]{PlainSaslServer.MECHANISM}; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java new file mode 100644 index 0000000000..f427cc7206 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java @@ -0,0 +1,36 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.state; + +/** + * States used in the AMQ protocol. Used by the finite state machine to determine + * valid responses. + */ +public enum AMQState +{ + CONNECTION_NOT_STARTED, + CONNECTION_NOT_AUTH, + CONNECTION_NOT_TUNED, + CONNECTION_NOT_OPENED, + CONNECTION_OPEN, + CONNECTION_CLOSING, + CONNECTION_CLOSED +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java new file mode 100644 index 0000000000..c5b3099f58 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java @@ -0,0 +1,263 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.state; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.handler.BasicAckMethodHandler; +import org.apache.qpid.server.handler.BasicCancelMethodHandler; +import org.apache.qpid.server.handler.BasicConsumeMethodHandler; +import org.apache.qpid.server.handler.BasicGetMethodHandler; +import org.apache.qpid.server.handler.BasicPublishMethodHandler; +import org.apache.qpid.server.handler.BasicQosHandler; +import org.apache.qpid.server.handler.BasicRecoverMethodHandler; +import org.apache.qpid.server.handler.BasicRejectMethodHandler; +import org.apache.qpid.server.handler.ChannelCloseHandler; +import org.apache.qpid.server.handler.ChannelCloseOkHandler; +import org.apache.qpid.server.handler.ChannelFlowHandler; +import org.apache.qpid.server.handler.ChannelOpenHandler; +import org.apache.qpid.server.handler.ConnectionCloseMethodHandler; +import org.apache.qpid.server.handler.ConnectionCloseOkMethodHandler; +import org.apache.qpid.server.handler.ConnectionOpenMethodHandler; +import org.apache.qpid.server.handler.ConnectionSecureOkMethodHandler; +import org.apache.qpid.server.handler.ConnectionStartOkMethodHandler; +import org.apache.qpid.server.handler.ConnectionTuneOkMethodHandler; +import org.apache.qpid.server.handler.ExchangeBoundHandler; +import org.apache.qpid.server.handler.ExchangeDeclareHandler; +import org.apache.qpid.server.handler.ExchangeDeleteHandler; +import org.apache.qpid.server.handler.QueueBindHandler; +import org.apache.qpid.server.handler.QueueDeclareHandler; +import org.apache.qpid.server.handler.QueueDeleteHandler; +import org.apache.qpid.server.handler.QueuePurgeHandler; +import org.apache.qpid.server.handler.TxCommitHandler; +import org.apache.qpid.server.handler.TxRollbackHandler; +import org.apache.qpid.server.handler.TxSelectHandler; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +/** + * The state manager is responsible for managing the state of the protocol session.

For each AMQProtocolHandler + * there is a separate state manager. + */ +public class AMQStateManager implements AMQMethodListener +{ + private static final Logger _logger = Logger.getLogger(AMQStateManager.class); + + private final VirtualHostRegistry _virtualHostRegistry; + private final AMQProtocolSession _protocolSession; + /** The current state */ + private AMQState _currentState; + + /** + * Maps from an AMQState instance to a Map from Class to StateTransitionHandler. The class must be a subclass of + * AMQFrame. + */ +/* private final EnumMap, StateAwareMethodListener>> _state2HandlersMap = + new EnumMap, StateAwareMethodListener>>( + AMQState.class); + */ + + + private CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet(); + + public AMQStateManager(VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession) + { + + _virtualHostRegistry = virtualHostRegistry; + _protocolSession = protocolSession; + _currentState = AMQState.CONNECTION_NOT_STARTED; + + } + + /* + protected void registerListeners() + { + Map, StateAwareMethodListener> frame2handlerMap; + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_AUTH, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + frame2handlerMap.put(ConnectionOpenBody.class, ConnectionOpenMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap); + + // + // ConnectionOpen handlers + // + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + ChannelOpenHandler.getInstance(); + ChannelCloseHandler.getInstance(); + ChannelCloseOkHandler.getInstance(); + ConnectionCloseMethodHandler.getInstance(); + ConnectionCloseOkMethodHandler.getInstance(); + ConnectionTuneOkMethodHandler.getInstance(); + ConnectionSecureOkMethodHandler.getInstance(); + ConnectionStartOkMethodHandler.getInstance(); + ExchangeDeclareHandler.getInstance(); + ExchangeDeleteHandler.getInstance(); + ExchangeBoundHandler.getInstance(); + BasicAckMethodHandler.getInstance(); + BasicRecoverMethodHandler.getInstance(); + BasicConsumeMethodHandler.getInstance(); + BasicGetMethodHandler.getInstance(); + BasicCancelMethodHandler.getInstance(); + BasicPublishMethodHandler.getInstance(); + BasicQosHandler.getInstance(); + QueueBindHandler.getInstance(); + QueueDeclareHandler.getInstance(); + QueueDeleteHandler.getInstance(); + QueuePurgeHandler.getInstance(); + ChannelFlowHandler.getInstance(); + TxSelectHandler.getInstance(); + TxCommitHandler.getInstance(); + TxRollbackHandler.getInstance(); + BasicRejectMethodHandler.getInstance(); + + _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + + _state2HandlersMap.put(AMQState.CONNECTION_CLOSING, frame2handlerMap); + + } */ + + public AMQState getCurrentState() + { + return _currentState; + } + + public void changeState(AMQState newState) throws AMQException + { + _logger.debug("State changing to " + newState + " from old state " + _currentState); + final AMQState oldState = _currentState; + _currentState = newState; + + for (StateListener l : _stateListeners) + { + l.stateChanged(oldState, newState); + } + } + + public void error(Exception e) + { + _logger.error("State manager received error notification[Current State:" + _currentState + "]: " + e, e); + for (StateListener l : _stateListeners) + { + l.error(e); + } + } + + public boolean methodReceived(AMQMethodEvent evt) throws AMQException + { + MethodDispatcher dispatcher = _protocolSession.getMethodDispatcher(); + + final int channelId = evt.getChannelId(); + B body = evt.getMethod(); + + if(channelId != 0 && _protocolSession.getChannel(channelId)== null) + { + + if(! ((body instanceof ChannelOpenBody) + || (body instanceof ChannelCloseOkBody) + || (body instanceof ChannelCloseBody))) + { + throw body.getConnectionException(AMQConstant.CHANNEL_ERROR, "channel is closed"); + } + + } + + return body.execute(dispatcher, channelId); + + } + + private void checkChannel(AMQMethodEvent evt, AMQProtocolSession protocolSession) + throws AMQException + { + if ((evt.getChannelId() != 0) && !(evt.getMethod() instanceof ChannelOpenBody) + && (protocolSession.getChannel(evt.getChannelId()) == null) + && !protocolSession.channelAwaitingClosure(evt.getChannelId())) + { + throw evt.getMethod().getChannelNotFoundException(evt.getChannelId()); + } + } + +/* + protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, + B frame) + // throws IllegalStateTransitionException + { + final Map, StateAwareMethodListener> classToHandlerMap = + _state2HandlersMap.get(currentState); + + final StateAwareMethodListener handler = + (classToHandlerMap == null) ? null : (StateAwareMethodListener) classToHandlerMap.get(frame.getClass()); + + if (handler == null) + { + _logger.debug("No state transition handler defined for receiving frame " + frame); + + return null; + } + else + { + return handler; + } + } +*/ + + public void addStateListener(StateListener listener) + { + _logger.debug("Adding state listener"); + _stateListeners.add(listener); + } + + public void removeStateListener(StateListener listener) + { + _stateListeners.remove(listener); + } + + public VirtualHostRegistry getVirtualHostRegistry() + { + return _virtualHostRegistry; + } + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java new file mode 100644 index 0000000000..cec67a8a6d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.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.state; + +import org.apache.qpid.AMQException; + +/** + * @todo Not an AMQP exception as no status code. + * + * @todo Not used! Delete. + */ +public class IllegalStateTransitionException extends AMQException +{ + private AMQState _originalState; + + private Class _frame; + + public IllegalStateTransitionException(AMQState originalState, Class frame) + { + super("No valid state transition defined for receiving frame " + frame + " from state " + originalState); + _originalState = originalState; + _frame = frame; + } + + public AMQState getOriginalState() + { + return _originalState; + } + + public Class getFrameClass() + { + return _frame; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java new file mode 100644 index 0000000000..3c11bb8a9c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.state; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +/** + * A frame listener that is informed of the protocol state when invoked and has + * the opportunity to update state. + * + */ +public interface StateAwareMethodListener +{ + void methodReceived(AMQStateManager stateManager, B evt, int channelId) throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java new file mode 100644 index 0000000000..00fc09867b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.state; + +import org.apache.qpid.AMQException; + +public interface StateListener +{ + void stateChanged(AMQState oldState, AMQState newState) throws AMQException; + + void error(Throwable t); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java new file mode 100644 index 0000000000..743a736884 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java @@ -0,0 +1,1463 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.store; + +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.queue.QueueRegistry; + +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.mina.common.ByteBuffer; + +import java.io.File; +import java.io.ByteArrayInputStream; +import java.sql.DriverManager; +import java.sql.Driver; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Blob; +import java.sql.Types; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.TreeMap; + + +public class DerbyMessageStore implements MessageStore +{ + + private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class); + + private static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; + + + private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; + + private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; + + private static final String EXCHANGE_TABLE_NAME = "QPID_EXCHANGE"; + private static final String QUEUE_TABLE_NAME = "QPID_QUEUE"; + private static final String BINDINGS_TABLE_NAME = "QPID_BINDINGS"; + private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRY"; + private static final String MESSAGE_META_DATA_TABLE_NAME = "QPID_MESSAGE_META_DATA"; + private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; + + private static final int DB_VERSION = 1; + + + + private VirtualHost _virtualHost; + private static Class DRIVER_CLASS; + + private final AtomicLong _messageId = new AtomicLong(1); + private AtomicBoolean _closed = new AtomicBoolean(false); + + private String _connectionURL; + + + + private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; + private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; + private static final String CREATE_EXCHANGE_TABLE = "CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )"; + private static final String CREATE_QUEUE_TABLE = "CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), PRIMARY KEY ( name ) )"; + private static final String CREATE_BINDINGS_TABLE = "CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )"; + private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )"; + private static final String CREATE_MESSAGE_META_DATA_TABLE = "CREATE TABLE "+MESSAGE_META_DATA_TABLE_NAME+" ( message_id bigint not null, exchange_name varchar(255) not null, routing_key varchar(255), flag_mandatory smallint not null, flag_immediate smallint not null, content_header blob, chunk_count int not null, PRIMARY KEY ( message_id ) )"; + private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, chunk_id int not null, content_chunk blob , PRIMARY KEY (message_id, chunk_id) )"; + private static final String SELECT_FROM_QUEUE = "SELECT name, owner FROM " + QUEUE_TABLE_NAME; + private static final String SELECT_FROM_EXCHANGE = "SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME; + private static final String SELECT_FROM_BINDINGS = + "SELECT queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ?"; + private static final String DELETE_FROM_MESSAGE_META_DATA = "DELETE FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?"; + private static final String INSERT_INTO_EXCHANGE = "INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )"; + private static final String DELETE_FROM_EXCHANGE = "DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; + private static final String INSERT_INTO_BINDINGS = "INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )"; + private static final String DELETE_FROM_BINDINGS = "DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?"; + private static final String INSERT_INTO_QUEUE = "INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner) VALUES (?, ?)"; + private static final String DELETE_FROM_QUEUE = "DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; + private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)"; + private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?"; + private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, chunk_id, content_chunk ) values (?, ?, ?)"; + private static final String INSERT_INTO_MESSAGE_META_DATA = "INSERT INTO " + MESSAGE_META_DATA_TABLE_NAME + "( message_id , exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count ) values (?, ?, ?, ?, ?, ?, ?)"; + private static final String SELECT_FROM_MESSAGE_META_DATA = + "SELECT exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String SELECT_FROM_MESSAGE_CONTENT = + "SELECT content_chunk FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? and chunk_id = ?"; + private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME; + private static final String TABLE_EXISTANCE_QUERY = "SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"; + + + private enum State + { + INITIAL, + CONFIGURING, + RECOVERING, + STARTED, + CLOSING, + CLOSED + } + + private State _state = State.INITIAL; + + + public void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception + { + stateTransition(State.INITIAL, State.CONFIGURING); + + initialiseDriver(); + + _virtualHost = virtualHost; + + _logger.info("Configuring Derby message store for virtual host " + virtualHost.getName()); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + final String databasePath = config.getString(base + "." + ENVIRONMENT_PATH_PROPERTY, "derbyDB"); + + File environmentPath = new File(databasePath); + if (!environmentPath.exists()) + { + if (!environmentPath.mkdirs()) + { + throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " + + "Ensure the path is correct and that the permissions are correct."); + } + } + + createOrOpenDatabase(databasePath); + + // this recovers durable queues and persistent messages + + recover(); + + stateTransition(State.RECOVERING, State.STARTED); + + } + + private static synchronized void initialiseDriver() throws ClassNotFoundException + { + if(DRIVER_CLASS == null) + { + DRIVER_CLASS = (Class) Class.forName(SQL_DRIVER_NAME); + } + } + + private void createOrOpenDatabase(final String environmentPath) throws SQLException + { + _connectionURL = "jdbc:derby:" + environmentPath + "/" + _virtualHost.getName() + ";create=true"; + + Connection conn = newConnection(); + + createVersionTable(conn); + createExchangeTable(conn); + createQueueTable(conn); + createBindingsTable(conn); + createQueueEntryTable(conn); + createMessageMetaDataTable(conn); + createMessageContentTable(conn); + + conn.close(); + } + + + + private void createVersionTable(final Connection conn) throws SQLException + { + if(!tableExists(DB_VERSION_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + + stmt.execute(CREATE_DB_VERSION_TABLE); + stmt.close(); + + PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); + pstmt.setInt(1, DB_VERSION); + pstmt.execute(); + pstmt.close(); + } + + } + + + private void createExchangeTable(final Connection conn) throws SQLException + { + if(!tableExists(EXCHANGE_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + + stmt.execute(CREATE_EXCHANGE_TABLE); + stmt.close(); + } + } + + private void createQueueTable(final Connection conn) throws SQLException + { + if(!tableExists(QUEUE_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + stmt.execute(CREATE_QUEUE_TABLE); + stmt.close(); + } + } + + private void createBindingsTable(final Connection conn) throws SQLException + { + if(!tableExists(BINDINGS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + stmt.execute(CREATE_BINDINGS_TABLE); + + stmt.close(); + } + + } + + private void createQueueEntryTable(final Connection conn) throws SQLException + { + if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + stmt.execute(CREATE_QUEUE_ENTRY_TABLE); + + stmt.close(); + } + + } + + private void createMessageMetaDataTable(final Connection conn) throws SQLException + { + if(!tableExists(MESSAGE_META_DATA_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + stmt.execute(CREATE_MESSAGE_META_DATA_TABLE); + + stmt.close(); + } + + } + + + private void createMessageContentTable(final Connection conn) throws SQLException + { + if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); + + stmt.close(); + } + + } + + + + private boolean tableExists(final String tableName, final Connection conn) throws SQLException + { + PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); + stmt.setString(1, tableName); + ResultSet rs = stmt.executeQuery(); + boolean exists = rs.next(); + rs.close(); + stmt.close(); + return exists; + } + + public void recover() throws AMQException + { + stateTransition(State.CONFIGURING, State.RECOVERING); + + _logger.info("Recovering persistent state..."); + StoreContext context = new StoreContext(); + + try + { + Map queues = loadQueues(); + + recoverExchanges(); + + try + { + + beginTran(context); + + deliverMessages(context, queues); + _logger.info("Persistent state recovered successfully"); + commitTran(context); + + } + finally + { + if(inTran(context)) + { + abortTran(context); + } + } + } + catch (SQLException e) + { + + throw new AMQException("Error recovering persistent state: " + e, e); + } + + } + + private Map loadQueues() throws SQLException, AMQException + { + Connection conn = newConnection(); + + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE); + Map queueMap = new HashMap(); + while(rs.next()) + { + String queueName = rs.getString(1); + String owner = rs.getString(2); + AMQShortString queueNameShortString = new AMQShortString(queueName); + AMQQueue q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost, + null); + _virtualHost.getQueueRegistry().registerQueue(q); + queueMap.put(queueNameShortString,q); + + } + return queueMap; + } + + private void recoverExchanges() throws AMQException, SQLException + { + for (Exchange exchange : loadExchanges()) + { + recoverExchange(exchange); + } + } + + + private List loadExchanges() throws AMQException, SQLException + { + + List exchanges = new ArrayList(); + Connection conn = null; + try + { + conn = newConnection(); + + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(SELECT_FROM_EXCHANGE); + + Exchange exchange; + while(rs.next()) + { + String exchangeName = rs.getString(1); + String type = rs.getString(2); + boolean autoDelete = rs.getShort(3) != 0; + + exchange = _virtualHost.getExchangeFactory().createExchange(new AMQShortString(exchangeName), new AMQShortString(type), true, autoDelete, 0); + _virtualHost.getExchangeRegistry().registerExchange(exchange); + exchanges.add(exchange); + + } + return exchanges; + + } + finally + { + if(conn != null) + { + conn.close(); + } + } + + } + + private void recoverExchange(Exchange exchange) throws AMQException, SQLException + { + _logger.info("Recovering durable exchange " + exchange.getName() + " of type " + exchange.getType() + "..."); + + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + Connection conn = null; + try + { + conn = newConnection(); + + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_BINDINGS); + stmt.setString(1, exchange.getName().toString()); + + ResultSet rs = stmt.executeQuery(); + + + while(rs.next()) + { + String queueName = rs.getString(1); + String bindingKey = rs.getString(2); + Blob arguments = rs.getBlob(3); + + + AMQQueue queue = queueRegistry.getQueue(new AMQShortString(queueName)); + if (queue == null) + { + _logger.error("Unkown queue: " + queueName + " cannot be bound to exchange: " + + exchange.getName()); + } + else + { + _logger.info("Restoring binding: (Exchange: " + exchange.getName() + ", Queue: " + queueName + + ", Routing Key: " + bindingKey + ", Arguments: " + arguments + + ")"); + + FieldTable argumentsFT = null; + if(arguments != null) + { + byte[] argumentBytes = arguments.getBytes(0, (int) arguments.length()); + ByteBuffer buf = ByteBuffer.wrap(argumentBytes); + argumentsFT = new FieldTable(buf,arguments.length()); + } + + queue.bind(exchange, bindingKey == null ? null : new AMQShortString(bindingKey), argumentsFT); + + } + } + } + finally + { + if(conn != null) + { + conn.close(); + } + } + } + + public void close() throws Exception + { + _closed.getAndSet(true); + } + + public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException + { + + boolean localTx = getOrCreateTransaction(storeContext); + + Connection conn = getConnection(storeContext); + ConnectionWrapper wrapper = (ConnectionWrapper) storeContext.getPayload(); + + + if (_logger.isDebugEnabled()) + { + _logger.debug("Message Id: " + messageId + " Removing"); + } + + // first we need to look up the header to get the chunk count + MessageMetaData mmd = getMessageMetaData(storeContext, messageId); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_META_DATA); + stmt.setLong(1,messageId); + wrapper.setRequiresCommit(); + int results = stmt.executeUpdate(); + + if (results == 0) + { + if (localTx) + { + abortTran(storeContext); + } + + throw new AMQException("Message metadata not found for message id " + messageId); + } + stmt.close(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Deleted metadata for message " + messageId); + } + + stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + results = stmt.executeUpdate(); + + if(results != mmd.getContentChunkCount()) + { + if (localTx) + { + abortTran(storeContext); + } + throw new AMQException("Unexpected number of content chunks when deleting message. Expected " + mmd.getContentChunkCount() + " but found " + results); + + } + + if (localTx) + { + commitTran(storeContext); + } + } + catch (SQLException e) + { + if ((conn != null) && localTx) + { + abortTran(storeContext); + } + + throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e); + } + + } + + public void createExchange(Exchange exchange) throws AMQException + { + if (_state != State.RECOVERING) + { + try + { + Connection conn = null; + + try + { + conn = newConnection(); + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_EXCHANGE); + stmt.setString(1, exchange.getName().toString()); + stmt.setString(2, exchange.getType().toString()); + stmt.setShort(3, exchange.isAutoDelete() ? (short) 1 : (short) 0); + stmt.execute(); + stmt.close(); + conn.commit(); + + } + finally + { + if(conn != null) + { + conn.close(); + } + } + } + catch (SQLException e) + { + throw new AMQException("Error writing Exchange with name " + exchange.getName() + " to database: " + e, e); + } + } + + } + + public void removeExchange(Exchange exchange) throws AMQException + { + Connection conn = null; + + try + { + conn = newConnection(); + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_EXCHANGE); + stmt.setString(1, exchange.getName().toString()); + int results = stmt.executeUpdate(); + if(results == 0) + { + throw new AMQException("Exchange " + exchange.getName() + " not found"); + } + else + { + conn.commit(); + stmt.close(); + } + } + catch (SQLException e) + { + throw new AMQException("Error writing deleting with name " + exchange.getName() + " from database: " + e, e); + } + finally + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error(e); + } + } + + } + } + + public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) + throws AMQException + { + if (_state != State.RECOVERING) + { + Connection conn = null; + + + try + { + conn = newConnection(); + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_BINDINGS); + stmt.setString(1, exchange.getName().toString() ); + stmt.setString(2, queue.getName().toString()); + stmt.setString(3, routingKey == null ? null : routingKey.toString()); + if(args != null) + { + /* This would be the Java 6 way of setting a Blob + Blob blobArgs = conn.createBlob(); + blobArgs.setBytes(0, args.getDataAsBytes()); + stmt.setBlob(4, blobArgs); + */ + byte[] bytes = args.getDataAsBytes(); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + stmt.setBinaryStream(4, bis, bytes.length); + } + else + { + stmt.setNull(4, Types.BLOB); + } + + stmt.executeUpdate(); + conn.commit(); + stmt.close(); + } + catch (SQLException e) + { + throw new AMQException("Error writing binding for AMQQueue with name " + queue.getName() + " to exchange " + + exchange.getName() + " to database: " + e, e); + } + finally + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error(e); + } + } + + } + + } + + + } + + public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) + throws AMQException + { + Connection conn = null; + + + try + { + conn = newConnection(); + // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); + stmt.setString(1, exchange.getName().toString() ); + stmt.setString(2, queue.getName().toString()); + stmt.setString(3, routingKey == null ? null : routingKey.toString()); + + + if(stmt.executeUpdate() != 1) + { + throw new AMQException("Queue binding for queue with name " + queue.getName() + " to exchange " + + exchange.getName() + " not found"); + } + conn.commit(); + stmt.close(); + } + catch (SQLException e) + { + throw new AMQException("Error removing binding for AMQQueue with name " + queue.getName() + " to exchange " + + exchange.getName() + " in database: " + e, e); + } + finally + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error(e); + } + } + + } + + + } + + public void createQueue(AMQQueue queue) throws AMQException + { + createQueue(queue, null); + } + + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQException + { + _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called"); + + if (_state != State.RECOVERING) + { + try + { + Connection conn = newConnection(); + + PreparedStatement stmt = + conn.prepareStatement(INSERT_INTO_QUEUE); + + stmt.setString(1, queue.getName().toString()); + stmt.setString(2, queue.getOwner() == null ? null : queue.getOwner().toString()); + + stmt.execute(); + + stmt.close(); + + conn.commit(); + + conn.close(); + } + catch (SQLException e) + { + throw new AMQException("Error writing AMQQueue with name " + queue.getName() + " to database: " + e, e); + } + } + } + + private Connection newConnection() throws SQLException + { + final Connection connection = DriverManager.getConnection(_connectionURL); + return connection; + } + + public void removeQueue(final AMQQueue queue) throws AMQException + { + AMQShortString name = queue.getName(); + _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); + Connection conn = null; + + + try + { + conn = newConnection(); + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE); + stmt.setString(1, name.toString()); + int results = stmt.executeUpdate(); + + + if (results == 0) + { + throw new AMQException("Queue " + name + " not found"); + } + + conn.commit(); + stmt.close(); + } + catch (SQLException e) + { + throw new AMQException("Error writing deleting with name " + name + " from database: " + e, e); + } + finally + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error(e); + } + } + + } + + + } + + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + AMQShortString name = queue.getName(); + + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + try + { + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); + stmt.setString(1,name.toString()); + stmt.setLong(2,messageId); + stmt.executeUpdate(); + connWrapper.requiresCommit(); + + if(localTx) + { + commitTran(context); + } + + + + if (_logger.isDebugEnabled()) + { + _logger.debug("Enqueuing message " + messageId + " on queue " + name + "[Connection" + conn + "]"); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + _logger.error("Failed to enqueue: " + e, e); + throw new AMQException("Error writing enqueued message with id " + messageId + " for queue " + name + + " to database", e); + } + + } + + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + AMQShortString name = queue.getName(); + + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); + stmt.setString(1,name.toString()); + stmt.setLong(2,messageId); + int results = stmt.executeUpdate(); + + connWrapper.requiresCommit(); + + if(results != 1) + { + throw new AMQException("Unable to find message with id " + messageId + " on queue " + name); + } + + if(localTx) + { + commitTran(context); + } + + + + if (_logger.isDebugEnabled()) + { + _logger.debug("Dequeuing message " + messageId + " on queue " + name + "[Connection" + conn + "]"); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + _logger.error("Failed to dequeue: " + e, e); + throw new AMQException("Error deleting enqueued message with id " + messageId + " for queue " + name + + " from database", e); + } + + } + + private static final class ConnectionWrapper + { + private final Connection _connection; + private boolean _requiresCommit; + + public ConnectionWrapper(Connection conn) + { + _connection = conn; + } + + public void setRequiresCommit() + { + _requiresCommit = true; + } + + public boolean requiresCommit() + { + return _requiresCommit; + } + + public Connection getConnection() + { + return _connection; + } + } + + public void beginTran(StoreContext context) throws AMQException + { + if (context.getPayload() != null) + { + throw new AMQException("Fatal internal error: transactional context is not empty at beginTran: " + + context.getPayload()); + } + else + { + try + { + Connection conn = newConnection(); + + + context.setPayload(new ConnectionWrapper(conn)); + } + catch (SQLException e) + { + throw new AMQException("Error starting transaction: " + e, e); + } + } + } + + public void commitTran(StoreContext context) throws AMQException + { + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + if (connWrapper == null) + { + throw new AMQException("Fatal internal error: transactional context is empty at commitTran"); + } + + try + { + Connection conn = connWrapper.getConnection(); + if(connWrapper.requiresCommit()) + { + conn.commit(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("commit tran completed"); + } + + } + conn.close(); + } + catch (SQLException e) + { + throw new AMQException("Error commit tx: " + e, e); + } + finally + { + context.setPayload(null); + } + } + + public void abortTran(StoreContext context) throws AMQException + { + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + if (connWrapper == null) + { + throw new AMQException("Fatal internal error: transactional context is empty at abortTran"); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("abort tran called: " + connWrapper.getConnection()); + } + + try + { + Connection conn = connWrapper.getConnection(); + if(connWrapper.requiresCommit()) + { + conn.rollback(); + } + + conn.close(); + } + catch (SQLException e) + { + throw new AMQException("Error aborting transaction: " + e, e); + } + finally + { + context.setPayload(null); + } + } + + public boolean inTran(StoreContext context) + { + return context.getPayload() != null; + } + + public Long getNewMessageId() + { + return _messageId.getAndIncrement(); + } + + public void storeContentBodyChunk(StoreContext context, + Long messageId, + int index, + ContentChunk contentBody, + boolean lastContentBody) throws AMQException + { + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + try + { + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + stmt.setInt(2, index); + byte[] chunkData = new byte[contentBody.getSize()]; + contentBody.getData().duplicate().get(chunkData); + /* this would be the Java 6 way of doing things + Blob dataAsBlob = conn.createBlob(); + dataAsBlob.setBytes(1L, chunkData); + stmt.setBlob(3, dataAsBlob); + */ + ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); + stmt.setBinaryStream(3, bis, chunkData.length); + stmt.executeUpdate(); + connWrapper.requiresCommit(); + + if(localTx) + { + commitTran(context); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + + throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e); + } + + } + + public void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData mmd) + throws AMQException + { + + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload(); + + try + { + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_META_DATA); + stmt.setLong(1,messageId); + stmt.setString(2, mmd.getMessagePublishInfo().getExchange().toString()); + stmt.setString(3, mmd.getMessagePublishInfo().getRoutingKey().toString()); + stmt.setShort(4, mmd.getMessagePublishInfo().isMandatory() ? (short) 1 : (short) 0); + stmt.setShort(5, mmd.getMessagePublishInfo().isImmediate() ? (short) 1 : (short) 0); + + ContentHeaderBody headerBody = mmd.getContentHeaderBody(); + final int bodySize = headerBody.getSize(); + byte[] underlying = new byte[bodySize]; + ByteBuffer buf = ByteBuffer.wrap(underlying); + headerBody.writePayload(buf); +/* + Blob dataAsBlob = conn.createBlob(); + dataAsBlob.setBytes(1L, underlying); + stmt.setBlob(6, dataAsBlob); +*/ + ByteArrayInputStream bis = new ByteArrayInputStream(underlying); + stmt.setBinaryStream(6,bis,underlying.length); + + stmt.setInt(7, mmd.getContentChunkCount()); + + stmt.executeUpdate(); + connWrapper.requiresCommit(); + + if(localTx) + { + commitTran(context); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + + throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e); + } + + + } + + public MessageMetaData getMessageMetaData(StoreContext context, Long messageId) throws AMQException + { + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + + + try + { + + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_META_DATA); + stmt.setLong(1,messageId); + ResultSet rs = stmt.executeQuery(); + + if(rs.next()) + { + final AMQShortString exchange = new AMQShortString(rs.getString(1)); + final AMQShortString routingKey = rs.getString(2) == null ? null : new AMQShortString(rs.getString(2)); + final boolean mandatory = (rs.getShort(3) != (short)0); + final boolean immediate = (rs.getShort(4) != (short)0); + MessagePublishInfo info = new MessagePublishInfo() + { + + public AMQShortString getExchange() + { + return exchange; + } + + public void setExchange(AMQShortString exchange) + { + + } + + public boolean isImmediate() + { + return immediate; + } + + public boolean isMandatory() + { + return mandatory; + } + + public AMQShortString getRoutingKey() + { + return routingKey; + } + } ; + + Blob dataAsBlob = rs.getBlob(5); + + byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); + ByteBuffer buf = ByteBuffer.wrap(dataAsBytes); + + ContentHeaderBody chb = ContentHeaderBody.createFromBuffer(buf, dataAsBytes.length); + + if(localTx) + { + commitTran(context); + } + + return new MessageMetaData(info, chb, rs.getInt(6)); + + } + else + { + if(localTx) + { + abortTran(context); + } + throw new AMQException("Metadata not found for message with id " + messageId); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + + throw new AMQException("Error reading AMQMessage with id " + messageId + " from database: " + e, e); + } + + + } + + public ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException + { + boolean localTx = getOrCreateTransaction(context); + Connection conn = getConnection(context); + + + try + { + + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + stmt.setInt(2, index); + ResultSet rs = stmt.executeQuery(); + + if(rs.next()) + { + Blob dataAsBlob = rs.getBlob(1); + + final int size = (int) dataAsBlob.length(); + byte[] dataAsBytes = dataAsBlob.getBytes(1, size); + final ByteBuffer buf = ByteBuffer.wrap(dataAsBytes); + + ContentChunk cb = new ContentChunk() + { + + public int getSize() + { + return size; + } + + public ByteBuffer getData() + { + return buf; + } + + public void reduceToFit() + { + + } + }; + + if(localTx) + { + commitTran(context); + } + + return cb; + + } + else + { + if(localTx) + { + abortTran(context); + } + throw new AMQException("Message not found for message with id " + messageId); + } + } + catch (SQLException e) + { + if(localTx) + { + abortTran(context); + } + + throw new AMQException("Error reading AMQMessage with id " + messageId + " from database: " + e, e); + } + + + + } + + public boolean isPersistent() + { + return true; + } + + private void checkNotClosed() throws MessageStoreClosedException + { + if (_closed.get()) + { + throw new MessageStoreClosedException(); + } + } + + + private static final class ProcessAction + { + private final AMQQueue _queue; + private final StoreContext _context; + private final AMQMessage _message; + + public ProcessAction(AMQQueue queue, StoreContext context, AMQMessage message) + { + _queue = queue; + _context = context; + _message = message; + } + + public void process() throws AMQException + { + _queue.enqueue(_context, _message); + + } + + } + + + private void deliverMessages(final StoreContext context, Map queues) + throws SQLException, AMQException + { + Map msgMap = new HashMap(); + List actions = new ArrayList(); + + Map queueRecoveries = new TreeMap(); + + final boolean inLocaltran = inTran(context); + Connection conn = null; + try + { + + if(inLocaltran) + { + conn = getConnection(context); + } + else + { + conn = newConnection(); + } + + + MessageHandleFactory messageHandleFactory = new MessageHandleFactory(); + long maxId = 1; + + TransactionalContext txnContext = new NonTransactionalContext(this, new StoreContext(), null, null); + + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); + + + while (rs.next()) + { + + + + AMQShortString queueName = new AMQShortString(rs.getString(1)); + + + AMQQueue queue = queues.get(queueName); + if (queue == null) + { + queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost, null); + + _virtualHost.getQueueRegistry().registerQueue(queue); + queues.put(queueName, queue); + } + + long messageId = rs.getLong(2); + maxId = Math.max(maxId, messageId); + AMQMessage message = msgMap.get(messageId); + + if(message != null) + { + message.incrementReference(); + } + else + { + message = new AMQMessage(messageId, this, messageHandleFactory, txnContext); + msgMap.put(messageId,message); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("On recovery, delivering " + message.getMessageId() + " to " + queue.getName()); + } + + if (_logger.isInfoEnabled()) + { + Integer count = queueRecoveries.get(queueName); + if (count == null) + { + count = 0; + } + + queueRecoveries.put(queueName, ++count); + + } + + actions.add(new ProcessAction(queue, context, message)); + + } + + for(ProcessAction action : actions) + { + action.process(); + } + + _messageId.set(maxId + 1); + } + catch (SQLException e) + { + _logger.error("Error: " + e, e); + throw e; + } + finally + { + if (inLocaltran && conn != null) + { + conn.close(); + } + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Recovered message counts: " + queueRecoveries); + } + } + + private Connection getConnection(final StoreContext context) + { + return ((ConnectionWrapper)context.getPayload()).getConnection(); + } + + private boolean getOrCreateTransaction(StoreContext context) throws AMQException + { + + ConnectionWrapper tx = (ConnectionWrapper) context.getPayload(); + if (tx == null) + { + beginTran(context); + return true; + } + + return false; + } + + private synchronized void stateTransition(State requiredState, State newState) throws AMQException + { + if (_state != requiredState) + { + throw new AMQException("Cannot transition to the state: " + newState + "; need to be in state: " + requiredState + + "; currently in state: " + _state); + } + + _state = newState; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java new file mode 100644 index 0000000000..587c85fc12 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java @@ -0,0 +1,234 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +/** A simple message store that stores the messages in a threadsafe structure in memory. */ +public class MemoryMessageStore implements MessageStore +{ + private static final Logger _log = Logger.getLogger(MemoryMessageStore.class); + + private static final int DEFAULT_HASHTABLE_CAPACITY = 50000; + + private static final String HASHTABLE_CAPACITY_CONFIG = "hashtable-capacity"; + + protected ConcurrentMap _metaDataMap; + + protected ConcurrentMap> _contentBodyMap; + + private final AtomicLong _messageId = new AtomicLong(1); + private AtomicBoolean _closed = new AtomicBoolean(false); + + public void configure() + { + _log.info("Using capacity " + DEFAULT_HASHTABLE_CAPACITY + " for hash tables"); + _metaDataMap = new ConcurrentHashMap(DEFAULT_HASHTABLE_CAPACITY); + _contentBodyMap = new ConcurrentHashMap>(DEFAULT_HASHTABLE_CAPACITY); + } + + public void configure(String base, Configuration config) + { + int hashtableCapacity = config.getInt(base + "." + HASHTABLE_CAPACITY_CONFIG, DEFAULT_HASHTABLE_CAPACITY); + _log.info("Using capacity " + hashtableCapacity + " for hash tables"); + _metaDataMap = new ConcurrentHashMap(hashtableCapacity); + _contentBodyMap = new ConcurrentHashMap>(hashtableCapacity); + } + + public void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception + { + configure(base, config); + } + + public void close() throws Exception + { + _closed.getAndSet(true); + if (_metaDataMap != null) + { + _metaDataMap.clear(); + _metaDataMap = null; + } + if (_contentBodyMap != null) + { + _contentBodyMap.clear(); + _contentBodyMap = null; + } + } + + public void removeMessage(StoreContext context, Long messageId) throws AMQException + { + checkNotClosed(); + if (_log.isDebugEnabled()) + { + _log.debug("Removing message with id " + messageId); + } + _metaDataMap.remove(messageId); + _contentBodyMap.remove(messageId); + } + + public void createExchange(Exchange exchange) throws AMQException + { + + } + + public void removeExchange(Exchange exchange) throws AMQException + { + + } + + public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + + } + + public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException + { + + } + + + public void createQueue(AMQQueue queue) throws AMQException + { + // Not requred to do anything + } + + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQException + { + // Not required to do anything + } + + public void removeQueue(final AMQQueue queue) throws AMQException + { + // Not required to do anything + } + + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + // Not required to do anything + } + + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + // Not required to do anything + } + + public void beginTran(StoreContext context) throws AMQException + { + // Not required to do anything + } + + public void commitTran(StoreContext context) throws AMQException + { + // Not required to do anything + } + + public void abortTran(StoreContext context) throws AMQException + { + // Not required to do anything + } + + public boolean inTran(StoreContext context) + { + return false; + } + + public List createQueues() throws AMQException + { + return null; + } + + public Long getNewMessageId() + { + return _messageId.getAndIncrement(); + } + + public void storeContentBodyChunk(StoreContext context, Long messageId, int index, ContentChunk contentBody, boolean lastContentBody) + throws AMQException + { + checkNotClosed(); + List bodyList = _contentBodyMap.get(messageId); + + if (bodyList == null && lastContentBody) + { + _contentBodyMap.put(messageId, Collections.singletonList(contentBody)); + } + else + { + if (bodyList == null) + { + bodyList = new ArrayList(); + _contentBodyMap.put(messageId, bodyList); + } + + bodyList.add(index, contentBody); + } + } + + public void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData messageMetaData) + throws AMQException + { + checkNotClosed(); + _metaDataMap.put(messageId, messageMetaData); + } + + public MessageMetaData getMessageMetaData(StoreContext context, Long messageId) throws AMQException + { + checkNotClosed(); + return _metaDataMap.get(messageId); + } + + public ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException + { + checkNotClosed(); + List bodyList = _contentBodyMap.get(messageId); + return bodyList.get(index); + } + + public boolean isPersistent() + { + return false; + } + + private void checkNotClosed() throws MessageStoreClosedException + { + if (_closed.get()) + { + throw new MessageStoreClosedException(); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java new file mode 100644 index 0000000000..f2910acb77 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -0,0 +1,276 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.commons.configuration.Configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * MessageStore defines the interface to a storage area, which can be used to preserve the state of messages, queues + * and exchanges in a transactional manner. + * + *

All message store, remove, enqueue and dequeue operations are carried out against a {@link StoreContext} which + * encapsulates the transactional context they are performed in. Many such operations can be carried out in a single + * transaction. + * + *

The storage and removal of queues and exchanges, are not carried out in a transactional context. + * + *

+ *
CRC Card
Responsibilities + *
Accept transaction boundary demarcations: Begin, Commit, Abort. + *
Store and remove queues. + *
Store and remove exchanges. + *
Store and remove messages. + *
Bind and unbind queues to exchanges. + *
Enqueue and dequeue messages to queues. + *
Generate message identifiers. + *
+ */ +public interface MessageStore +{ + /** + * Called after instantiation in order to configure the message store. A particular implementation can define + * whatever parameters it wants. + * + * @param virtualHost The virtual host using by this store + * @param base The base element identifier from which all configuration items are relative. For example, if + * the base element is "store", the all elements used by concrete classes will be "store.foo" etc. + * @param config The apache commons configuration object. + * + * @throws Exception If any error occurs that means the store is unable to configure itself. + */ + void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception; + + /** + * Called to close and cleanup any resources used by the message store. + * + * @throws Exception If the close fails. + */ + void close() throws Exception; + + /** + * Removes the specified message from the store in the given transactional store context. + * + * @param storeContext The transactional context to remove the message in. + * @param messageId Identifies the message to remove. + * + * @throws AMQException If the operation fails for any reason. + */ + void removeMessage(StoreContext storeContext, Long messageId) throws AMQException; + + /** + * Makes the specified exchange persistent. + * + * @param exchange The exchange to persist. + * + * @throws AMQException If the operation fails for any reason. + */ + void createExchange(Exchange exchange) throws AMQException; + + /** + * Removes the specified persistent exchange. + * + * @param exchange The exchange to remove. + * + * @throws AMQException If the operation fails for any reason. + */ + void removeExchange(Exchange exchange) throws AMQException; + + /** + * Binds the specified queue to an exchange with a routing key. + * + * @param exchange The exchange to bind to. + * @param routingKey The routing key to bind by. + * @param queue The queue to bind. + * @param args Additional parameters. + * + * @throws AMQException If the operation fails for any reason. + */ + void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException; + + /** + * Unbinds the specified from an exchange under a particular routing key. + * + * @param exchange The exchange to unbind from. + * @param routingKey The routing key to unbind. + * @param queue The queue to unbind. + * @param args Additonal parameters. + * + * @throws AMQException If the operation fails for any reason. + */ + void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException; + + /** + * Makes the specified queue persistent. + * + * @param queue The queue to store. + * + * @throws AMQException If the operation fails for any reason. + */ + void createQueue(AMQQueue queue) throws AMQException; + + /** + * Makes the specified queue persistent. + * + * @param queue The queue to store. + * + * @param arguments The additional arguments to the binding + * @throws AMQException If the operation fails for any reason. + */ + void createQueue(AMQQueue queue, FieldTable arguments) throws AMQException; + + /** + * Removes the specified queue from the persistent store. + * + * @param queue The queue to remove. + * @throws AMQException If the operation fails for any reason. + */ + void removeQueue(final AMQQueue queue) throws AMQException; + + /** + * Places a message onto a specified queue, in a given transactional context. + * + * @param context The transactional context for the operation. + * @param queue The queue to place the message on. + * @param messageId The message to enqueue. + * @throws AMQException If the operation fails for any reason. + */ + void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException; + + /** + * Extracts a message from a specified queue, in a given transactional context. + * + * @param context The transactional context for the operation. + * @param queue The queue to place the message on. + * @param messageId The message to dequeue. + * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. + */ + void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException; + + /** + * Begins a transactional context. + * + * @param context The transactional context to begin. + * + * @throws AMQException If the operation fails for any reason. + */ + void beginTran(StoreContext context) throws AMQException; + + /** + * Commits all operations performed within a given transactional context. + * + * @param context The transactional context to commit all operations for. + * + * @throws AMQException If the operation fails for any reason. + */ + void commitTran(StoreContext context) throws AMQException; + + /** + * Abandons all operations performed within a given transactional context. + * + * @param context The transactional context to abandon. + * + * @throws AMQException If the operation fails for any reason. + */ + void abortTran(StoreContext context) throws AMQException; + + /** + * Tests a transactional context to see if it has been begun but not yet committed or aborted. + * + * @param context The transactional context to test. + * + * @return true if the transactional context is live, false otherwise. + */ + boolean inTran(StoreContext context); + + /** + * Return a valid, currently unused message id. + * + * @return A fresh message id. + */ + Long getNewMessageId(); + + /** + * Stores a chunk of message data. + * + * @param context The transactional context for the operation. + * @param messageId The message to store the data for. + * @param index The index of the data chunk. + * @param contentBody The content of the data chunk. + * @param lastContentBody Flag to indicate that this is the last such chunk for the message. + * + * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. + */ + void storeContentBodyChunk(StoreContext context, Long messageId, int index, ContentChunk contentBody, + boolean lastContentBody) throws AMQException; + + /** + * Stores message meta-data. + * + * @param context The transactional context for the operation. + * @param messageId The message to store the data for. + * @param messageMetaData The message meta data to store. + * + * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. + */ + void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData messageMetaData) throws AMQException; + + /** + * Retrieves message meta-data. + * + * @param context The transactional context for the operation. + * @param messageId The message to get the meta-data for. + * + * @return The message meta data. + * + * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. + */ + MessageMetaData getMessageMetaData(StoreContext context, Long messageId) throws AMQException; + + /** + * Retrieves a chunk of message data. + * + * @param context The transactional context for the operation. + * @param messageId The message to get the data chunk for. + * @param index The offset index of the data chunk within the message. + * + * @return A chunk of message data. + * + * @throws AMQException If the operation fails for any reason, or if the specified message does not exist. + */ + ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException; + + /** + * Is this store capable of persisting the data + * + * @return true if this store is capable of persisting data + */ + boolean isPersistent(); + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreClosedException.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreClosedException.java new file mode 100644 index 0000000000..3d1538c7eb --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreClosedException.java @@ -0,0 +1,36 @@ +package org.apache.qpid.server.store; + +import org.apache.qpid.AMQException;/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +/** + * NOTE: this class currently extends AMQException but + * we should be using AMQExceptions internally in the code base for Protocol errors hence + * the message store interface should throw a different super class which this should be + * moved to reflect + */ +public class MessageStoreClosedException extends AMQException +{ + public MessageStoreClosedException() + { + super("Message store closed"); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java new file mode 100644 index 0000000000..fdb56a1a55 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.log4j.Logger; + +/** + * A context that the store can use to associate with a transactional context. For example, it could store + * some kind of txn id. + * + * @author Apache Software Foundation + */ +public class StoreContext +{ + private static final Logger _logger = Logger.getLogger(StoreContext.class); + + private String _name; + private Object _payload; + + public StoreContext() + { + _name = "StoreContext"; + } + + public StoreContext(String name) + { + _name = name; + } + + public Object getPayload() + { + return _payload; + } + + public void setPayload(Object payload) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("public void setPayload(Object payload = " + payload + "): called"); + } + _payload = payload; + } + + /** + * Prints out the transactional context as a string, mainly for debugging purposes. + * + * @return The transactional context as a string. + */ + public String toString() + { + return "<_name = " + _name + ", _payload = " + _payload + ">"; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java new file mode 100644 index 0000000000..fbc8b3af7d --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java @@ -0,0 +1,29 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.subscription; + +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.AMQException; + +public interface ClientDeliveryMethod +{ + void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java new file mode 100644 index 0000000000..e2ed4104de --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.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.subscription; + +import org.apache.qpid.server.queue.QueueEntry; + +public interface RecordDeliveryMethod +{ + void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java new file mode 100644 index 0000000000..9419572399 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -0,0 +1,96 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscription; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; + +public interface Subscription +{ + + + public static enum State + { + ACTIVE, + SUSPENDED, + CLOSED + } + + public static interface StateListener + { + public void stateChange(Subscription sub, State oldState, State newState); + } + + AMQQueue getQueue(); + + QueueEntry.SubscriptionAcquiredState getOwningState(); + + void setQueue(AMQQueue queue); + + AMQChannel getChannel(); + + AMQShortString getConsumerTag(); + + boolean isSuspended(); + + boolean hasInterest(QueueEntry msg); + + boolean isAutoClose(); + + boolean isClosed(); + + boolean isBrowser(); + + void close(); + + boolean filtersMessages(); + + void send(QueueEntry msg) throws AMQException; + + void queueDeleted(AMQQueue queue); + + + boolean wouldSuspend(QueueEntry msg); + + void getSendLock(); + void releaseSendLock(); + + void resend(final QueueEntry entry) throws AMQException; + + void restoreCredit(final QueueEntry queueEntry); + + void setStateListener(final StateListener listener); + + public State getState(); + + QueueEntry getLastSeenEntry(); + + boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue); + + + boolean isActive(); + + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java new file mode 100644 index 0000000000..ce0362d73f --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.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.subscription; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.AMQChannel; + +/** + * Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory + * primarily assists testing although in future more sophisticated subscribers may need a different subscription + * implementation. + * + * @see org.apache.qpid.server.queue.AMQQueue + */ +public interface SubscriptionFactory +{ + Subscription createSubscription(int channel, + AMQProtocolSession protocolSession, + AMQShortString consumerTag, + boolean acks, + FieldTable filters, + boolean noLocal, FlowCreditManager creditManager) throws AMQException; + + + Subscription createSubscription(AMQChannel channel, + AMQProtocolSession protocolSession, + AMQShortString consumerTag, + boolean acks, + FieldTable filters, + boolean noLocal, + FlowCreditManager creditManager, + ClientDeliveryMethod clientMethod, + RecordDeliveryMethod recordMethod + ) + throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java new file mode 100644 index 0000000000..5badbad642 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java @@ -0,0 +1,103 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscription; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionFactory; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.common.AMQPFilterTypes; + +public class SubscriptionFactoryImpl implements SubscriptionFactory +{ + + /* private SubscriptionFactoryImpl() + { + + }*/ + + public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession, + AMQShortString consumerTag, boolean acks, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager) throws AMQException + { + AMQChannel channel = protocolSession.getChannel(channelId); + if (channel == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); + } + ClientDeliveryMethod clientMethod = channel.getClientDeliveryMethod(); + RecordDeliveryMethod recordMethod = channel.getRecordDeliveryMethod(); + + + return createSubscription(channel, protocolSession, consumerTag, acks, filters, + noLocal, + creditManager, + clientMethod, + recordMethod + ); + } + + public Subscription createSubscription(final AMQChannel channel, + final AMQProtocolSession protocolSession, + final AMQShortString consumerTag, + final boolean acks, + final FieldTable filters, + final boolean noLocal, + final FlowCreditManager creditManager, + final ClientDeliveryMethod clientMethod, + final RecordDeliveryMethod recordMethod + ) + throws AMQException + { + boolean isBrowser; + + if (filters != null) + { + Boolean isBrowserObj = (Boolean) filters.get(AMQPFilterTypes.NO_CONSUME.getValue()); + isBrowser = (isBrowserObj != null) && isBrowserObj.booleanValue(); + } + else + { + isBrowser = false; + } + + if(isBrowser) + { + return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + } + else if(acks) + { + return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + } + else + { + return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + } + } + + + public static final SubscriptionFactoryImpl INSTANCE = new SubscriptionFactoryImpl(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java new file mode 100644 index 0000000000..a616c2ea35 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -0,0 +1,617 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscription; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.filter.FilterManager; +import org.apache.qpid.server.filter.FilterManagerFactory; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.StoreContext; + +/** + * Encapsulation of a supscription to a queue.

Ties together the protocol session of a subscriber, the consumer tag + * that was given out by the broker and the channel id.

+ */ +public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener +{ + + private StateListener _stateListener = new StateListener() + { + + public void stateChange(Subscription sub, State oldState, State newState) + { + + } + }; + + + private final AtomicReference _state = new AtomicReference(State.ACTIVE); + private final AtomicReference _queueContext = new AtomicReference(null); + private final ClientDeliveryMethod _deliveryMethod; + private final RecordDeliveryMethod _recordMethod; + + private QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); + private final Lock _stateChangeLock; + + static final class BrowserSubscription extends SubscriptionImpl + { + public BrowserSubscription(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + } + + + public boolean isBrowser() + { + return true; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param msg The message to send + * @throws AMQException + */ + @Override + public void send(QueueEntry msg) throws AMQException + { + // We don't decrement the reference here as we don't want to consume the message + // but we do want to send it to the client. + + synchronized (getChannel()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + sendToClient(msg, deliveryTag); + } + + } + + @Override + public boolean wouldSuspend(QueueEntry msg) + { + return false; + } + + } + + public static class NoAckSubscription extends SubscriptionImpl + { + public NoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + } + + + public boolean isBrowser() + { + return false; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param entry The message to send + * @throws AMQException + */ + @Override + public void send(QueueEntry entry) throws AMQException + { + + StoreContext storeContext = getChannel().getStoreContext(); + try + { // 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. + entry.dequeue(storeContext); + + + synchronized (getChannel()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + + sendToClient(entry, deliveryTag); + + } + entry.dispose(storeContext); + } + finally + { + //Only set delivered if it actually was writen successfully.. + // using a try->finally would set it even if an error occured. + // Is this what we want? + + entry.setDeliveredToSubscription(); + } + } + + @Override + public boolean wouldSuspend(QueueEntry msg) + { + return false; + } + + } + + static final class AckSubscription extends SubscriptionImpl + { + public AckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + } + + + public boolean isBrowser() + { + return false; + } + + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param entry The message to send + * @throws AMQException + */ + @Override + public void send(QueueEntry entry) throws AMQException + { + + try + { // 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()) + { + long deliveryTag = getChannel().getNextDeliveryTag(); + + + recordMessageDelivery(entry, deliveryTag); + sendToClient(entry, deliveryTag); + + + } + } + finally + { + //Only set delivered if it actually was writen successfully.. + // using a try->finally would set it even if an error occured. + // Is this what we want? + + entry.setDeliveredToSubscription(); + } + } + + + } + + + private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class); + + private final AMQChannel _channel; + + private final AMQShortString _consumerTag; + + + private final boolean _noLocal; + + private final FlowCreditManager _creditManager; + + private FilterManager _filters; + + private final Boolean _autoClose; + + + private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); + + private AMQQueue _queue; + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + + + + public SubscriptionImpl(AMQChannel channel , AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable arguments, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + + _channel = channel; + _consumerTag = consumerTag; + + _creditManager = creditManager; + creditManager.addStateListener(this); + + _noLocal = noLocal; + + + _filters = FilterManagerFactory.createManager(arguments); + + _deliveryMethod = deliveryMethod; + _recordMethod = recordMethod; + + + _stateChangeLock = new ReentrantLock(); + + + if (arguments != null) + { + Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); + if (autoClose != null) + { + _autoClose = (Boolean) autoClose; + } + else + { + _autoClose = false; + } + } + else + { + _autoClose = false; + } + + + } + + + + public synchronized void setQueue(AMQQueue queue) + { + if(getQueue() != null) + { + throw new IllegalStateException("Attempt to set queue for subscription " + this + " to " + queue + "when already set to " + getQueue()); + } + _queue = queue; + } + + public String toString() + { + String subscriber = "[channel=" + _channel + + ", consumerTag=" + _consumerTag + + ", session=" + getProtocolSession().getKey() ; + + return subscriber + "]"; + } + + /** + * This method can be called by each of the publisher threads. As a result all changes to the channel object must be + * thread safe. + * + * @param msg The message to send + * @throws AMQException + */ + abstract public void send(QueueEntry msg) throws AMQException; + + + public boolean isSuspended() + { + return !isActive() || _channel.isSuspended() || _deleted.get(); + } + + /** + * Callback indicating that a queue has been deleted. + * + * @param queue The queue to delete + */ + public void queueDeleted(AMQQueue queue) + { + _deleted.set(true); +// _channel.queueDeleted(queue); + } + + public boolean filtersMessages() + { + return _filters != null || _noLocal; + } + + public boolean hasInterest(QueueEntry entry) + { + //check that the message hasn't been rejected + if (entry.isRejectedBy(this)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + entry.debugIdentity()); + } +// return false; + } + + + + //todo - client id should be recoreded and this test removed but handled below + if (_noLocal) + { + final Object publisherId = entry.getMessage().getPublisherClientInstance(); + + // We don't want local messages so check to see if message is one we sent + Object localInstance; + + if (publisherId != null && (getProtocolSession().getClientProperties() != null) && + (localInstance = getProtocolSession().getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) + { + if(publisherId.equals(localInstance)) + { + return false; + } + } + else + { + + localInstance = getProtocolSession().getClientIdentifier(); + //todo - client id should be recoreded and this test removed but handled here + + + if (localInstance != null && localInstance.equals(entry.getMessage().getPublisherIdentifier())) + { + return false; + } + } + + + } + + + if (_logger.isDebugEnabled()) + { + _logger.debug("(" + debugIdentity() + ") checking filters for message (" + entry.debugIdentity()); + } + return checkFilters(entry); + + } + + private String id = String.valueOf(System.identityHashCode(this)); + + private String debugIdentity() + { + return id; + } + + private boolean checkFilters(QueueEntry msg) + { + return (_filters == null) || _filters.allAllow(msg.getMessage()); + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + + + public void close() + { + boolean closed = false; + State state = getState(); + + _stateChangeLock.lock(); + try + { + while(!closed && state != State.CLOSED) + { + closed = _state.compareAndSet(state, State.CLOSED); + if(!closed) + { + state = getState(); + } + else + { + _stateListener.stateChange(this,state, State.CLOSED); + } + } + _creditManager.removeListener(this); + } + finally + { + _stateChangeLock.unlock(); + } + + + if (closed) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Called close() on a closed subscription"); + } + + return; + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing subscription (" + debugIdentity() + "):" + this); + } + } + + public boolean isClosed() + { + return getState() == State.CLOSED; + } + + + public boolean wouldSuspend(QueueEntry msg) + { + return !_creditManager.useCreditForMessage(msg.getMessage());//_channel.wouldSuspend(msg.getMessage()); + } + + public void getSendLock() + { + _stateChangeLock.lock(); + } + + public void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public void resend(final QueueEntry entry) throws AMQException + { + _queue.resend(entry, this); + } + + public AMQChannel getChannel() + { + return _channel; + } + + public AMQShortString getConsumerTag() + { + return _consumerTag; + } + + public AMQProtocolSession getProtocolSession() + { + return _channel.getProtocolSession(); + } + + public AMQQueue getQueue() + { + return _queue; + } + + public void restoreCredit(final QueueEntry queueEntry) + { + _creditManager.addCredit(1, queueEntry.getSize()); + } + + + public void creditStateChanged(boolean hasCredit) + { + + if(hasCredit) + { + if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) + { + _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); + } + else + { + // this is a hack to get round the issue of increasing bytes credit + _stateListener.stateChange(this, State.ACTIVE, State.ACTIVE); + } + } + else + { + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + } + } + + public State getState() + { + return _state.get(); + } + + + public void setStateListener(final StateListener listener) + { + _stateListener = listener; + } + + + public QueueEntry getLastSeenEntry() + { + return _queueContext.get(); + } + + public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newvalue) + { + return _queueContext.compareAndSet(expected,newvalue); + } + + + protected void sendToClient(final QueueEntry entry, final long deliveryTag) + throws AMQException + { + _deliveryMethod.deliverToClient(this,entry,deliveryTag); + } + + + protected void recordMessageDelivery(final QueueEntry entry, final long deliveryTag) + { + _recordMethod.recordMessageDelivery(this,entry,deliveryTag); + } + + + public boolean isActive() + { + return getState() == State.ACTIVE; + } + + public QueueEntry.SubscriptionAcquiredState getOwningState() + { + return _owningState; + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java new file mode 100644 index 0000000000..3fbb6bfa4a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java @@ -0,0 +1,247 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscription; + +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.subscription.Subscription; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.nio.ByteBuffer; + +public class SubscriptionList +{ + + private final SubscriptionNode _head = new SubscriptionNode(); + + private AtomicReference _tail = new AtomicReference(_head); + private final AMQQueue _queue; + private AtomicInteger _size = new AtomicInteger(); + + + public final class SubscriptionNode + { + private final AtomicBoolean _deleted = new AtomicBoolean(); + private final AtomicReference _next = new AtomicReference(); + private final Subscription _sub; + + + public SubscriptionNode() + { + + _sub = null; + _deleted.set(true); + } + + public SubscriptionNode(final Subscription sub) + { + _sub = sub; + } + + + public SubscriptionNode getNext() + { + + SubscriptionNode next = nextNode(); + while(next != null && next.isDeleted()) + { + + final SubscriptionNode newNext = next.nextNode(); + if(newNext != null) + { + _next.compareAndSet(next, newNext); + next = nextNode(); + } + else + { + next = null; + } + + } + return next; + } + + private SubscriptionNode nextNode() + { + return _next.get(); + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + + public boolean delete() + { + if(_deleted.compareAndSet(false,true)) + { + _size.decrementAndGet(); + advanceHead(); + return true; + } + else + { + return false; + } + } + + + public Subscription getSubscription() + { + return _sub; + } + } + + + public SubscriptionList(AMQQueue queue) + { + _queue = queue; + } + + private void advanceHead() + { + SubscriptionNode head = _head.nextNode(); + while(head._next.get() != null && head.isDeleted()) + { + + final SubscriptionNode newhead = head.nextNode(); + if(newhead != null) + { + _head._next.compareAndSet(head, newhead); + } + head = _head.nextNode(); + } + } + + + public SubscriptionNode add(Subscription sub) + { + SubscriptionNode node = new SubscriptionNode(sub); + for (;;) + { + SubscriptionNode tail = _tail.get(); + SubscriptionNode next = tail.nextNode(); + if (tail == _tail.get()) + { + if (next == null) + { + if (tail._next.compareAndSet(null, node)) + { + _tail.compareAndSet(tail, node); + _size.incrementAndGet(); + return node; + } + } + else + { + _tail.compareAndSet(tail, next); + } + } + } + + } + + public boolean remove(Subscription sub) + { + SubscriptionNode node = _head.getNext(); + while(node != null) + { + if(sub.equals(node._sub) && node.delete()) + { + return true; + } + node = node.getNext(); + } + return false; + } + + + public class SubscriptionNodeIterator + { + + private SubscriptionNode _lastNode; + + SubscriptionNodeIterator(SubscriptionNode startNode) + { + _lastNode = startNode; + } + + + public boolean atTail() + { + return _lastNode.nextNode() == null; + } + + public SubscriptionNode getNode() + { + + return _lastNode; + + } + + public boolean advance() + { + + if(!atTail()) + { + SubscriptionNode nextNode = _lastNode.nextNode(); + while(nextNode.isDeleted() && nextNode.nextNode() != null) + { + nextNode = nextNode.nextNode(); + } + _lastNode = nextNode; + return true; + + } + else + { + return false; + } + + } + + } + + + public SubscriptionNodeIterator iterator() + { + return new SubscriptionNodeIterator(_head); + } + + + public SubscriptionNode getHead() + { + return _head; + } + + public int size() + { + return _size.get(); + } + + + +} + + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java new file mode 100644 index 0000000000..b67bb98e28 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.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.transport; + +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.qpid.configuration.Configured; +import org.apache.log4j.Logger; + +public class ConnectorConfiguration +{ + private static final Logger _logger = Logger.getLogger(ConnectorConfiguration.class); + + public static final String DEFAULT_PORT = "5672"; + + public static final String SSL_PORT = "8672"; + + @Configured(path = "connector.processors", + defaultValue = "4") + public int processors; + + @Configured(path = "connector.port", + defaultValue = DEFAULT_PORT) + public int port; + + @Configured(path = "connector.bind", + defaultValue = "wildcard") + public String bindAddress; + + @Configured(path = "connector.socketReceiveBuffer", + defaultValue = "32767") + public int socketReceiveBufferSize; + + @Configured(path = "connector.socketWriteBuffer", + defaultValue = "32767") + public int socketWriteBuferSize; + + @Configured(path = "connector.tcpNoDelay", + defaultValue = "true") + public boolean tcpNoDelay; + + @Configured(path = "advanced.filterchain[@enableExecutorPool]", + defaultValue = "false") + public boolean enableExecutorPool; + + @Configured(path = "advanced.enablePooledAllocator", + defaultValue = "false") + public boolean enablePooledAllocator; + + @Configured(path = "advanced.enableDirectBuffers", + defaultValue = "false") + public boolean enableDirectBuffers; + + @Configured(path = "connector.ssl.enabled", + defaultValue = "false") + public boolean enableSSL; + + @Configured(path = "connector.ssl.sslOnly", + defaultValue = "true") + public boolean sslOnly; + + @Configured(path = "connector.ssl.port", + defaultValue = SSL_PORT) + public int sslPort; + + @Configured(path = "connector.ssl.keystorePath", + defaultValue = "none") + public String keystorePath; + + @Configured(path = "connector.ssl.keystorePassword", + defaultValue = "none") + public String keystorePassword; + + @Configured(path = "connector.ssl.certType", + defaultValue = "SunX509") + public String certType; + + @Configured(path = "connector.qpidnio", + defaultValue = "false") + public boolean _multiThreadNIO; + + @Configured(path = "advanced.useWriteBiasedPool", + defaultValue = "false") + public boolean useBiasedWrites; + + + public IoAcceptor createAcceptor() + { + if (_multiThreadNIO) + { + _logger.warn("Using Qpid Multithreaded IO Processing"); + return new org.apache.mina.transport.socket.nio.MultiThreadSocketAcceptor(processors, new NewThreadExecutor()); + } + else + { + _logger.warn("Using Mina IO Processing"); + return new org.apache.mina.transport.socket.nio.SocketAcceptor(processors, new NewThreadExecutor()); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java new file mode 100644 index 0000000000..bdd27f2d1c --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java @@ -0,0 +1,705 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.transport; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterAdapter; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoSession; +import org.apache.mina.util.BlockingQueue; +import org.apache.mina.util.ByteBufferUtil; +import org.apache.mina.util.IdentityHashSet; +import org.apache.mina.util.Queue; +import org.apache.mina.util.Stack; + +/** + * A Thread-pooling filter. This filter forwards {@link IoHandler} events + * to its thread pool. + *

+ * This is an implementation of + * Leader/Followers + * thread pool by Douglas C. Schmidt et al. + */ +public class ThreadPoolFilter extends IoFilterAdapter +{ + /** + * Default maximum size of thread pool (2G). + */ + public static final int DEFAULT_MAXIMUM_POOL_SIZE = Integer.MAX_VALUE; + + /** + * Default keep-alive time of thread pool (1 min). + */ + public static final int DEFAULT_KEEP_ALIVE_TIME = 60 * 1000; + + /** + * A queue which contains {@link Integer}s which represents reusable + * thread IDs. {@link Worker} first checks this queue and then + * uses {@link #threadId} when no reusable thread ID is available. + */ + private static final Queue threadIdReuseQueue = new Queue(); + private static int threadId = 0; + + private static int acquireThreadId() + { + synchronized (threadIdReuseQueue) + { + Integer id = (Integer) threadIdReuseQueue.pop(); + if (id == null) + { + return ++ threadId; + } + else + { + return id.intValue(); + } + } + } + + private static void releaseThreadId(int id) + { + synchronized (threadIdReuseQueue) + { + threadIdReuseQueue.push(new Integer(id)); + } + } + + private final String threadNamePrefix; + private final Map buffers = new IdentityHashMap(); + private final BlockingQueue unfetchedSessionBuffers = new BlockingQueue(); + private final Set allSessionBuffers = new IdentityHashSet(); + + private Worker leader; + private final Stack followers = new Stack(); + private final Set allWorkers = new IdentityHashSet(); + + private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE; + private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME; + + private boolean shuttingDown; + + private int poolSize; + private final Object poolSizeLock = new Object(); + + /** + * Creates a new instance of this filter with default thread pool settings. + */ + public ThreadPoolFilter() + { + this("IoThreadPool"); + } + + /** + * Creates a new instance of this filter with the specified thread name prefix + * and other default settings. + * + * @param threadNamePrefix the prefix of the thread names this pool will create. + */ + public ThreadPoolFilter(String threadNamePrefix) + { + if (threadNamePrefix == null) + { + throw new NullPointerException("threadNamePrefix"); + } + threadNamePrefix = threadNamePrefix.trim(); + if (threadNamePrefix.length() == 0) + { + throw new IllegalArgumentException("threadNamePrefix is empty."); + } + this.threadNamePrefix = threadNamePrefix; + } + + public String getThreadNamePrefix() + { + return threadNamePrefix; + } + + public int getPoolSize() + { + synchronized (poolSizeLock) + { + return poolSize; + } + } + + public int getMaximumPoolSize() + { + return maximumPoolSize; + } + + public int getKeepAliveTime() + { + return keepAliveTime; + } + + public void setMaximumPoolSize(int maximumPoolSize) + { + if (maximumPoolSize <= 0) + { + throw new IllegalArgumentException(); + } + this.maximumPoolSize = maximumPoolSize; + } + + public void setKeepAliveTime(int keepAliveTime) + { + this.keepAliveTime = keepAliveTime; + } + + public void init() + { + shuttingDown = false; + leader = new Worker(); + leader.start(); + leader.lead(); + } + + public void destroy() + { + shuttingDown = true; + int expectedPoolSize = 0; + while (getPoolSize() != expectedPoolSize) + { + List allWorkers; + synchronized (poolSizeLock) + { + allWorkers = new ArrayList(this.allWorkers); + } + + // You may not interrupt the current thread. + if (allWorkers.remove(Thread.currentThread())) + { + expectedPoolSize = 1; + } + + for (Iterator i = allWorkers.iterator(); i.hasNext();) + { + Worker worker = (Worker) i.next(); + while (worker.isAlive()) + { + worker.interrupt(); + try + { + // This timeout will help us from + // infinite lock-up and interrupt workers again. + worker.join(100); + } + catch (InterruptedException e) + { + } + } + } + } + + this.allSessionBuffers.clear(); + this.unfetchedSessionBuffers.clear(); + this.buffers.clear(); + this.followers.clear(); + this.leader = null; + } + + private void increasePoolSize(Worker worker) + { + synchronized (poolSizeLock) + { + poolSize++; + allWorkers.add(worker); + } + } + + private void decreasePoolSize(Worker worker) + { + synchronized (poolSizeLock) + { + poolSize--; + allWorkers.remove(worker); + } + } + + private void fireEvent(NextFilter nextFilter, IoSession session, + EventType type, Object data) + { + final BlockingQueue unfetchedSessionBuffers = this.unfetchedSessionBuffers; + final Set allSessionBuffers = this.allSessionBuffers; + final Event event = new Event(type, nextFilter, data); + + synchronized (unfetchedSessionBuffers) + { + final SessionBuffer buf = getSessionBuffer(session); + final Queue eventQueue = buf.eventQueue; + + synchronized (buf) + { + eventQueue.push(event); + } + + if (!allSessionBuffers.contains(buf)) + { + allSessionBuffers.add(buf); + unfetchedSessionBuffers.push(buf); + } + } + } + + /** + * Implement this method to fetch (or pop) a {@link SessionBuffer} from + * the given unfetchedSessionBuffers. The default implementation + * simply pops the buffer from it. You could prioritize the fetch order. + * + * @return A non-null {@link SessionBuffer} + */ + protected SessionBuffer fetchSessionBuffer(Queue unfetchedSessionBuffers) + { + return (SessionBuffer) unfetchedSessionBuffers.pop(); + } + + private SessionBuffer getSessionBuffer(IoSession session) + { + final Map buffers = this.buffers; + SessionBuffer buf = (SessionBuffer) buffers.get(session); + if (buf == null) + { + synchronized (buffers) + { + buf = (SessionBuffer) buffers.get(session); + if (buf == null) + { + buf = new SessionBuffer(session); + buffers.put(session, buf); + } + } + } + return buf; + } + + private void removeSessionBuffer(SessionBuffer buf) + { + final Map buffers = this.buffers; + final IoSession session = buf.session; + synchronized (buffers) + { + buffers.remove(session); + } + } + + protected static class SessionBuffer + { + private final IoSession session; + + private final Queue eventQueue = new Queue(); + + private SessionBuffer(IoSession session) + { + this.session = session; + } + + public IoSession getSession() + { + return session; + } + + public Queue getEventQueue() + { + return eventQueue; + } + } + + private class Worker extends Thread + { + private final int id; + private final Object promotionLock = new Object(); + private boolean dead; + + private Worker() + { + int id = acquireThreadId(); + this.id = id; + this.setName(threadNamePrefix + '-' + id); + increasePoolSize(this); + } + + public boolean lead() + { + final Object promotionLock = this.promotionLock; + synchronized (promotionLock) + { + if (dead) + { + return false; + } + + leader = this; + promotionLock.notify(); + } + + return true; + } + + public void run() + { + for (; ;) + { + if (!waitForPromotion()) + { + break; + } + + SessionBuffer buf = fetchBuffer(); + giveUpLead(); + if (buf == null) + { + break; + } + + processEvents(buf); + follow(); + releaseBuffer(buf); + } + + decreasePoolSize(this); + releaseThreadId(id); + } + + private SessionBuffer fetchBuffer() + { + BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers; + synchronized (unfetchedSessionBuffers) + { + while (!shuttingDown) + { + try + { + unfetchedSessionBuffers.waitForNewItem(); + } + catch (InterruptedException e) + { + continue; + } + + return ThreadPoolFilter.this.fetchSessionBuffer(unfetchedSessionBuffers); + } + } + + return null; + } + + private void processEvents(SessionBuffer buf) + { + final IoSession session = buf.session; + final Queue eventQueue = buf.eventQueue; + for (; ;) + { + Event event; + synchronized (buf) + { + event = (Event) eventQueue.pop(); + if (event == null) + { + break; + } + } + processEvent(event.getNextFilter(), session, + event.getType(), event.getData()); + } + } + + private void follow() + { + final Object promotionLock = this.promotionLock; + final Stack followers = ThreadPoolFilter.this.followers; + synchronized (promotionLock) + { + if (this != leader) + { + synchronized (followers) + { + followers.push(this); + } + } + } + } + + private void releaseBuffer(SessionBuffer buf) + { + final BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers; + final Set allSessionBuffers = ThreadPoolFilter.this.allSessionBuffers; + final Queue eventQueue = buf.eventQueue; + + synchronized (unfetchedSessionBuffers) + { + if (eventQueue.isEmpty()) + { + allSessionBuffers.remove(buf); + removeSessionBuffer(buf); + } + else + { + unfetchedSessionBuffers.push(buf); + } + } + } + + private boolean waitForPromotion() + { + final Object promotionLock = this.promotionLock; + + long startTime = System.currentTimeMillis(); + long currentTime = System.currentTimeMillis(); + + synchronized (promotionLock) + { + while (this != leader && !shuttingDown) + { + // Calculate remaining keep-alive time + int keepAliveTime = getKeepAliveTime(); + if (keepAliveTime > 0) + { + keepAliveTime -= (currentTime - startTime); + } + else + { + keepAliveTime = Integer.MAX_VALUE; + } + + // Break the loop if there's no remaining keep-alive time. + if (keepAliveTime <= 0) + { + break; + } + + // Wait for promotion + try + { + promotionLock.wait(keepAliveTime); + } + catch (InterruptedException e) + { + } + + // Update currentTime for the next iteration + currentTime = System.currentTimeMillis(); + } + + boolean timeToLead = this == leader && !shuttingDown; + + if (!timeToLead) + { + // time to die + synchronized (followers) + { + followers.remove(this); + } + + // Mark as dead explicitly when we've got promotionLock. + dead = true; + } + + return timeToLead; + } + } + + private void giveUpLead() + { + final Stack followers = ThreadPoolFilter.this.followers; + Worker worker; + do + { + synchronized (followers) + { + worker = (Worker) followers.pop(); + } + + if (worker == null) + { + // Increase the number of threads if we + // are not shutting down and we can increase the number. + if (!shuttingDown + && getPoolSize() < getMaximumPoolSize()) + { + worker = new Worker(); + worker.lead(); + worker.start(); + } + + // This loop should end because: + // 1) lead() is called already, + // 2) or it is shutting down and there's no more threads left. + break; + } + } + while (!worker.lead()); + } + } + + protected static class EventType + { + public static final EventType OPENED = new EventType("OPENED"); + + public static final EventType CLOSED = new EventType("CLOSED"); + + public static final EventType READ = new EventType("READ"); + + public static final EventType WRITTEN = new EventType("WRITTEN"); + + public static final EventType RECEIVED = new EventType("RECEIVED"); + + public static final EventType SENT = new EventType("SENT"); + + public static final EventType IDLE = new EventType("IDLE"); + + public static final EventType EXCEPTION = new EventType("EXCEPTION"); + + private final String value; + + private EventType(String value) + { + this.value = value; + } + + public String toString() + { + return value; + } + } + + protected static class Event + { + private final EventType type; + private final NextFilter nextFilter; + private final Object data; + + public Event(EventType type, NextFilter nextFilter, Object data) + { + this.type = type; + this.nextFilter = nextFilter; + this.data = data; + } + + public Object getData() + { + return data; + } + + + public NextFilter getNextFilter() + { + return nextFilter; + } + + + public EventType getType() + { + return type; + } + } + + public void sessionCreated(NextFilter nextFilter, IoSession session) + { + nextFilter.sessionCreated(session); + } + + public void sessionOpened(NextFilter nextFilter, + IoSession session) + { + fireEvent(nextFilter, session, EventType.OPENED, null); + } + + public void sessionClosed(NextFilter nextFilter, + IoSession session) + { + fireEvent(nextFilter, session, EventType.CLOSED, null); + } + + public void sessionIdle(NextFilter nextFilter, + IoSession session, IdleStatus status) + { + fireEvent(nextFilter, session, EventType.IDLE, status); + } + + public void exceptionCaught(NextFilter nextFilter, + IoSession session, Throwable cause) + { + fireEvent(nextFilter, session, EventType.EXCEPTION, cause); + } + + public void messageReceived(NextFilter nextFilter, + IoSession session, Object message) + { + ByteBufferUtil.acquireIfPossible(message); + fireEvent(nextFilter, session, EventType.RECEIVED, message); + } + + public void messageSent(NextFilter nextFilter, + IoSession session, Object message) + { + ByteBufferUtil.acquireIfPossible(message); + fireEvent(nextFilter, session, EventType.SENT, message); + } + + protected void processEvent(NextFilter nextFilter, + IoSession session, EventType type, + Object data) + { + if (type == EventType.RECEIVED) + { + nextFilter.messageReceived(session, data); + ByteBufferUtil.releaseIfPossible(data); + } + else if (type == EventType.SENT) + { + nextFilter.messageSent(session, data); + ByteBufferUtil.releaseIfPossible(data); + } + else if (type == EventType.EXCEPTION) + { + nextFilter.exceptionCaught(session, (Throwable) data); + } + else if (type == EventType.IDLE) + { + nextFilter.sessionIdle(session, (IdleStatus) data); + } + else if (type == EventType.OPENED) + { + nextFilter.sessionOpened(session); + } + else if (type == EventType.CLOSED) + { + nextFilter.sessionClosed(session); + } + } + + public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) + { + nextFilter.filterWrite(session, writeRequest); + } + + public void filterClose(NextFilter nextFilter, IoSession session) throws Exception + { + nextFilter.filterClose(session); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java new file mode 100644 index 0000000000..3c71282c57 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.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.txn; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.ack.TxAck; +import org.apache.qpid.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; + +import java.util.List; +import java.util.ArrayList; + +/** A transactional context that only supports local transactions. */ +public class LocalTransactionalContext implements TransactionalContext +{ + private static final Logger _log = Logger.getLogger(LocalTransactionalContext.class); + + private final TxnBuffer _txnBuffer = new TxnBuffer(); + + private final List _postCommitDeliveryList = new ArrayList(); + + /** + * We keep hold of the ack operation so that we can consolidate acks, i.e. multiple acks within a txn are + * consolidated into a single operation + */ + private TxAck _ackOp; + + private boolean _inTran = false; + + /** Are there messages to deliver. NOT Has the message been delivered */ + private boolean _messageDelivered = false; + private final AMQChannel _channel; + + + private abstract class DeliveryAction + { + + abstract public void process() throws AMQException; + + } + + private class RequeueAction extends DeliveryAction + { + public QueueEntry entry; + + public RequeueAction(QueueEntry entry) + { + this.entry = entry; + } + + public void process() throws AMQException + { + entry.requeue(getStoreContext()); + } + } + + private class PublishAction extends DeliveryAction + { + private final AMQQueue _queue; + private final AMQMessage _message; + + public PublishAction(final AMQQueue queue, final AMQMessage message) + { + _queue = queue; + _message = message; + } + + public void process() throws AMQException + { + + _message.incrementReference(); + try + { + QueueEntry entry = _queue.enqueue(getStoreContext(),_message); + + if(entry.immediateAndNotDelivered()) + { + getReturnMessages().add(new NoConsumersException(_message)); + } + } + finally + { + _message.decrementReference(getStoreContext()); + } + } + } + + public LocalTransactionalContext(final AMQChannel channel) + { + _channel = channel; + } + + public StoreContext getStoreContext() + { + return _channel.getStoreContext(); + } + + public List getReturnMessages() + { + return _channel.getReturnMessages(); + } + + public MessageStore getMessageStore() + { + return _channel.getMessageStore(); + } + + + public void rollback() throws AMQException + { + _txnBuffer.rollback(getStoreContext()); + // Hack to deal with uncommitted non-transactional writes + if (getMessageStore().inTran(getStoreContext())) + { + getMessageStore().abortTran(getStoreContext()); + _inTran = false; + } + + _postCommitDeliveryList.clear(); + } + + public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException + { + // A publication will result in the enlisting of several + // TxnOps. The first is an op that will store the message. + // Following that (and ordering is important), an op will + // be added for every queue onto which the message is + // enqueued. + _postCommitDeliveryList.add(new PublishAction(queue, message)); + _messageDelivered = true; + + } + + public void requeue(QueueEntry entry) throws AMQException + { + _postCommitDeliveryList.add(new RequeueAction(entry)); + _messageDelivered = true; + + } + + + private void checkAck(long deliveryTag, UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException + { + if (!unacknowledgedMessageMap.contains(deliveryTag)) + { + throw new AMQException("Ack with delivery tag " + deliveryTag + " not known for channel"); + } + } + + public void acknowledgeMessage(long deliveryTag, long lastDeliveryTag, boolean multiple, + UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException + { + // check that the tag exists to give early failure + if (!multiple || (deliveryTag > 0)) + { + checkAck(deliveryTag, unacknowledgedMessageMap); + } + // we use a single txn op for all acks and update this op + // as new acks come in. If this is the first ack in the txn + // we will need to create and enlist the op. + if (_ackOp == null) + { + _ackOp = new TxAck(unacknowledgedMessageMap); + _txnBuffer.enlist(_ackOp); + } + // update the op to include this ack request + if (multiple && (deliveryTag == 0)) + { + // if have signalled to ack all, that refers only + // to all at this time + _ackOp.update(lastDeliveryTag, multiple); + } + else + { + _ackOp.update(deliveryTag, multiple); + } + if(!_inTran && _ackOp.checkPersistent()) + { + beginTranIfNecessary(); + } + } + + public void messageFullyReceived(boolean persistent) throws AMQException + { + // Not required in this transactional context + } + + public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException + { + // Not required in this transactional context + } + + public void beginTranIfNecessary() throws AMQException + { + if (!_inTran) + { + if (_log.isDebugEnabled()) + { + _log.debug("Starting transaction on message store: " + this); + } + + getMessageStore().beginTran(getStoreContext()); + _inTran = true; + } + } + + public void commit() throws AMQException + { + if (_log.isDebugEnabled()) + { + _log.debug("Committing transactional context: " + this); + } + + if (_ackOp != null) + { + + _messageDelivered = true; + _ackOp.consolidate(); + // already enlisted, after commit will reset regardless of outcome + _ackOp = null; + } + + if (_messageDelivered && _inTran) + { + _txnBuffer.enlist(new StoreMessageOperation(getMessageStore())); + } + // fixme fail commit here ... QPID-440 + try + { + _txnBuffer.commit(getStoreContext()); + } + finally + { + _messageDelivered = false; + _inTran = getMessageStore().inTran(getStoreContext()); + } + + try + { + postCommitDelivery(); + } + catch (AMQException e) + { + // OK so what do we do now...? + _log.error("Failed to deliver messages following txn commit: " + e, e); + } + } + + private void postCommitDelivery() throws AMQException + { + if (_log.isDebugEnabled()) + { + _log.debug("Performing post commit delivery"); + } + + try + { + for (DeliveryAction dd : _postCommitDeliveryList) + { + dd.process(); + } + } + finally + { + _postCommitDeliveryList.clear(); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java new file mode 100644 index 0000000000..28af36e3db --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java @@ -0,0 +1,213 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.txn; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; + +/** @author Apache Software Foundation */ +public class NonTransactionalContext implements TransactionalContext +{ + private static final Logger _log = Logger.getLogger(NonTransactionalContext.class); + + /** Channel is useful for logging */ + private final AMQChannel _channel; + + /** Where to put undeliverable messages */ + private final List _returnMessages; + + + + private final MessageStore _messageStore; + + private final StoreContext _storeContext; + + /** Whether we are in a transaction */ + private boolean _inTran; + + public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel, + List returnMessages) + { + _channel = channel; + _storeContext = storeContext; + _returnMessages = returnMessages; + _messageStore = messageStore; + + } + + + public StoreContext getStoreContext() + { + return _storeContext; + } + + public void beginTranIfNecessary() throws AMQException + { + if (!_inTran) + { + _messageStore.beginTran(_storeContext); + _inTran = true; + } + } + + public void commit() throws AMQException + { + // Does not apply to this context + } + + public void rollback() throws AMQException + { + // Does not apply to this context + } + + public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException + { + QueueEntry entry = queue.enqueue(_storeContext, message); + + //following check implements the functionality + //required by the 'immediate' flag: + if(entry.immediateAndNotDelivered()) + { + _returnMessages.add(new NoConsumersException(entry.getMessage())); + } + + } + + public void requeue(QueueEntry entry) throws AMQException + { + entry.requeue(_storeContext); + } + + public void acknowledgeMessage(final long deliveryTag, long lastDeliveryTag, + boolean multiple, final UnacknowledgedMessageMap unacknowledgedMessageMap) + throws AMQException + { + + final boolean debug = _log.isDebugEnabled(); + ; + if (multiple) + { + if (deliveryTag == 0) + { + + //Spec 2.1.6.11 ... If the multiple field is 1, and the delivery tag is zero, + // tells the server to acknowledge all outstanding mesages. + _log.info("Multiple ack on delivery tag 0. ACKing all messages. Current count:" + + unacknowledgedMessageMap.size()); + unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + { + public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException + { + if (debug) + { + _log.debug("Discarding message: " + message.getMessage().getMessageId()); + } + if(message.getMessage().isPersistent()) + { + beginTranIfNecessary(); + } + //Message has been ack so discard it. This will dequeue and decrement the reference. + message.discard(_storeContext); + + return false; + } + + public void visitComplete() + { + unacknowledgedMessageMap.clear(); + } + }); + } + else + { + if (!unacknowledgedMessageMap.contains(deliveryTag)) + { + throw new AMQException("Multiple ack on delivery tag " + deliveryTag + " not known for channel"); + } + + unacknowledgedMessageMap.drainTo(deliveryTag, _storeContext); + } + } + else + { + QueueEntry msg; + msg = unacknowledgedMessageMap.get(deliveryTag); + + if (msg == null) + { + _log.info("Single ack on delivery tag " + deliveryTag + " not known for channel:" + + _channel.getChannelId()); + throw new AMQException("Single ack on delivery tag " + deliveryTag + " not known for channel:" + + _channel.getChannelId()); + } + + if (debug) + { + _log.debug("Discarding message: " + msg.getMessage().getMessageId()); + } + if(msg.getMessage().isPersistent()) + { + beginTranIfNecessary(); + } + + //Message has been ack so discard it. This will dequeue and decrement the reference. + msg.discard(_storeContext); + + unacknowledgedMessageMap.remove(deliveryTag); + + + if (debug) + { + _log.debug("Received non-multiple ack for messaging with delivery tag " + deliveryTag + " msg id " + + msg.getMessage().getMessageId()); + } + } + if(_inTran) + { + _messageStore.commitTran(_storeContext); + _inTran = false; + } + } + + public void messageFullyReceived(boolean persistent) throws AMQException + { + if (persistent) + { + _messageStore.commitTran(_storeContext); + _inTran = false; + } + } + + public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException + { + _channel.processReturns(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java new file mode 100644 index 0000000000..0e4d6c2030 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.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.txn; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; + +/** + * A transactional operation to store messages in an underlying persistent store. When this operation + * commits it will do everything to ensure that all messages are safely committed to persistent + * storage. + */ +public class StoreMessageOperation implements TxnOp +{ + private final MessageStore _messsageStore; + + public StoreMessageOperation(MessageStore messageStore) + { + _messsageStore = messageStore; + } + + public void prepare(StoreContext context) throws AMQException + { + } + + public void undoPrepare() + { + } + + public void commit(StoreContext context) throws AMQException + { + _messsageStore.commitTran(context); + } + + public void rollback(StoreContext context) throws AMQException + { + _messsageStore.abortTran(context); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java new file mode 100644 index 0000000000..647ba66fb4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.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.txn; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.StoreContext; + +/** + * TransactionalContext provides a context in which transactional operations on {@link AMQMessage}s are performed. + * Different levels of transactional support for the delivery of messages may be provided by different implementations + * of this interface. + * + *

The fundamental transactional operations that can be performed on a message queue are 'enqueue' and 'dequeue'. + * In this interface, these have been recast as the {@link #messageFullyReceived} and {@link #acknowledgeMessage} + * operations. This interface essentially provides a way to make enqueueing and dequeuing transactional. + * + *

+ *
CRC Card
Responsibilities + *
Explicitly accept a transaction start notification. + *
Commit all pending operations in a transaction. + *
Rollback all pending operations in a transaction. + *
Deliver a message to a queue as part of a transaction. + *
Redeliver a message to a queue as part of a transaction. + *
Mark a message as acknowledged as part of a transaction. + *
Accept notification that a message has been completely received as part of a transaction. + *
Accept notification that a message has been fully processed as part of a transaction. + *
Associate a message store context with this transaction context. + *
+ * + * @todo The 'fullyReceived' and 'messageProcessed' events sit uncomfortably in the responsibilities of a transactional + * context. They are non-transactional operations, used to trigger other side-effects. Consider moving them + * somewhere else, a seperate interface for example. + * + * @todo This transactional context could be written as a wrapper extension to a Queue implementation, that provides + * transactional management of the enqueue and dequeue operations, with added commit/rollback methods. Any + * queue implementation could be made transactional by wrapping it as a transactional queue. This would mean + * that the enqueue/dequeue operations do not need to be recast as deliver/acknowledge operations, which may be + * conceptually neater. + * + * For example: + *

+ * public interface Transactional
+ * {
+ *    public void commit();
+ *    public void rollback();
+ * }
+ *
+ * public interface TransactionalQueue extends Transactional, SizeableQueue
+ * {}
+ *
+ * public class Queues
+ * {
+ *    ...
+ *    // For transactional messaging, take a transactional view onto the queue.
+ *    public static  TransactionalQueue getTransactionalQueue(SizeableQueue queue) { ... }
+ *
+ *    // For non-transactional messaging, take a non-transactional view onto the queue.
+ *    public static  TransactionalQueue getNonTransactionalQueue(SizeableQueue queue) { ... }
+ * }
+ * 
+ */ +public interface TransactionalContext +{ + /** + * Explicitly begins the transaction, if it has not already been started. {@link #commit} or {@link #rollback} + * should automatically begin the next transaction in the chain. + * + * @throws AMQException If the transaction cannot be started for any reason. + */ + void beginTranIfNecessary() throws AMQException; + + /** + * Makes all pending operations on the transaction permanent and visible. + * + * @throws AMQException If the transaction cannot be committed for any reason. + */ + void commit() throws AMQException; + + /** + * Erases all pending operations on the transaction. + * + * @throws AMQException If the transaction cannot be committed for any reason. + */ + void rollback() throws AMQException; + + /** + * Delivers the specified message to the specified queue. + * + *

This is an 'enqueue' operation. + * + * @param queue + * @param message The message to deliver + * @throws AMQException If the message cannot be delivered for any reason. + */ + void deliver(final AMQQueue queue, AMQMessage message) throws AMQException; + + /** + * Requeues the specified message entry (message queue pair) + * + * + * @param queueEntry The message,queue pair + * + * @throws AMQException If the message cannot be delivered for any reason. + */ + void requeue(QueueEntry queueEntry) throws AMQException; + + + /** + * Acknowledges a message or many messages as delivered. All messages up to a specified one, may be acknowledged by + * setting the 'multiple' flag. It is also possible for the acknowledged message id to be zero, when the 'multiple' + * flag is set, in which case an acknowledgement up to the latest delivered message should be done. + * + *

This is a 'dequeue' operation. + * + * @param deliveryTag The id of the message to acknowledge, or zero, if using multiple acknowledgement + * up to the latest message. + * @param lastDeliveryTag The latest message delivered. + * @param multiple true if all message ids up the acknowledged one or latest delivered, are + * to be acknowledged, false otherwise. + * @param unacknowledgedMessageMap The unacknowledged messages in the transaction, to remove the acknowledged message + * from. + * + * @throws AMQException If the message cannot be acknowledged for any reason. + */ + void acknowledgeMessage(long deliveryTag, long lastDeliveryTag, boolean multiple, + UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException; + + /** + * Notifies the transactional context that a message has been fully received. The actual message that was received + * is not specified. This event may be used to trigger a process related to the receipt of the message, for example, + * flushing its data to disk. + * + * @param persistent true if the received message is persistent, false otherwise. + * + * @throws AMQException If the fully received event cannot be processed for any reason. + */ + void messageFullyReceived(boolean persistent) throws AMQException; + + /** + * Notifies the transactional context that a message has been delivered, succesfully or otherwise. The actual + * message that was delivered is not specified. This event may be used to trigger a process related to the + * outcome of the delivery of the message, for example, cleaning up failed deliveries. + * + * @param protocolSession The protocol session of the deliverable message. + * + * @throws AMQException If the message processed event cannot be handled for any reason. + */ + void messageProcessed(AMQProtocolSession protocolSession) throws AMQException; + + /** + * Gets the message store context associated with this transactional context. + * + * @return The message store context associated with this transactional context. + */ + StoreContext getStoreContext(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java new file mode 100644 index 0000000000..46a68b6a23 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.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.txn; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; + +/** Holds a list of TxnOp instance representing transactional operations. */ +public class TxnBuffer +{ + private final List _ops = new ArrayList(); + private static final Logger _log = Logger.getLogger(TxnBuffer.class); + + public TxnBuffer() + { + } + + public void commit(StoreContext context) throws AMQException + { + if (_log.isDebugEnabled()) + { + _log.debug("Committing " + _ops.size() + " ops to commit.:" + _ops); + } + + if (prepare(context)) + { + for (TxnOp op : _ops) + { + op.commit(context); + } + } + _ops.clear(); + } + + private boolean prepare(StoreContext context) throws AMQException + { + for (int i = 0; i < _ops.size(); i++) + { + TxnOp op = _ops.get(i); + try + { + op.prepare(context); + } + catch (AMQException e) + { + undoPrepare(i); + throw e; + } + catch (RuntimeException e) + { + undoPrepare(i); + throw e; + } + } + return true; + } + + private void undoPrepare(int lastPrepared) + { + //compensate previously prepared ops + for (int j = 0; j < lastPrepared; j++) + { + _ops.get(j).undoPrepare(); + } + } + + + + public void rollback(StoreContext context) throws AMQException + { + for (TxnOp op : _ops) + { + op.rollback(context); + } + _ops.clear(); + } + + public void enlist(TxnOp op) + { + _ops.add(op); + } + + public void cancel(TxnOp op) + { + _ops.remove(op); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java new file mode 100644 index 0000000000..919c078cf0 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.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.txn; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; + +/** + * This provides the abstraction of an individual operation within a + * transaction. It is used by the TxnBuffer class. + */ +public interface TxnOp +{ + /** + * Do the part of the operation that updates persistent state + */ + public void prepare(StoreContext context) throws AMQException; + /** + * Complete the operation started by prepare. Can now update in + * memory state or make netork transfers. + */ + public void commit(StoreContext context) throws AMQException; + /** + * This is not the same as rollback. Unfortunately the use of an + * in memory reference count as a locking mechanism and a test for + * whether a message should be deleted means that as things are, + * handling an acknowledgement unavoidably alters both memory and + * persistent state on prepare. This is needed to 'compensate' or + * undo the in-memory change if the peristent update of later ops + * fails. + */ + public void undoPrepare(); + /** + * Rolls back the operation. + */ + public void rollback(StoreContext context) throws AMQException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java new file mode 100644 index 0000000000..e730e2f3c3 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java @@ -0,0 +1,131 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util; + +import java.util.Iterator; + +import org.apache.log4j.Logger; + +public class CircularBuffer implements Iterable +{ + + private static final Logger _logger = Logger.getLogger(CircularBuffer.class); + + private final Object[] _log; + private int _size; + private int _index; + + public CircularBuffer(int size) + { + _log = new Object[size]; + } + + public void add(Object o) + { + _log[_index++] = o; + _size = Math.min(_size+1, _log.length); + if(_index >= _log.length) + { + _index = 0; + } + } + + public Object get(int i) + { + if(i >= _log.length) + { + throw new ArrayIndexOutOfBoundsException(i); + } + return _log[index(i)]; + } + + public int size() { + return _size; + } + + public Iterator iterator() + { + return new Iterator() + { + private int i = 0; + + public boolean hasNext() + { + return i < _size; + } + + public Object next() + { + return get(i++); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + public String toString() + { + StringBuilder s = new StringBuilder(); + boolean first = true; + for(Object o : this) + { + if(!first) + { + s.append(", "); + } + else + { + first = false; + } + s.append(o); + } + return s.toString(); + } + + public void dump() + { + for(Object o : this) + { + _logger.info(o); + } + } + + int index(int i) + { + return _size == _log.length ? (_index + i) % _log.length : i; + } + + public static void main(String[] artgv) + { + String[] items = new String[]{ + "A","B","C","D","E","F","G","H","I","J","K" + }; + CircularBuffer buffer = new CircularBuffer(5); + for(String s : items) + { + buffer.add(s); + _logger.info(buffer); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java new file mode 100644 index 0000000000..cf5e71a6e2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.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.util; + +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ConcurrentLinkedQueueNoSize extends ConcurrentLinkedQueue +{ + public int size() + { + if (isEmpty()) + { + return 0; + } + else + { + return 1; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java new file mode 100644 index 0000000000..eda97e0ed2 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java @@ -0,0 +1,105 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; + +/** + * Dynamic proxy that records invocations in a fixed size circular buffer, + * dumping details on hitting an exception. + *

+ * Useful in debugging. + *

+ */ +public class LoggingProxy implements InvocationHandler +{ + private final Object _target; + private final CircularBuffer _log; + + public LoggingProxy(Object target, int size) + { + _target = target; + _log = new CircularBuffer(size); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + try + { + entered(method, args); + Object result = method.invoke(_target, args); + returned(method, result); + return result; + } + catch(InvocationTargetException e) + { + dump(); + throw e.getTargetException(); + } + } + + void dump() + { + _log.dump(); + } + + CircularBuffer getBuffer() + { + return _log; + } + + private synchronized void entered(Method method, Object[] args) + { + if (args == null) + { + _log.add(Thread.currentThread() + ": " + method.getName() + "() entered"); + } + else + { + _log.add(Thread.currentThread() + ": " + method.getName() + "(" + Arrays.toString(args) + ") entered"); + } + } + + private synchronized void returned(Method method, Object result) + { + if (method.getReturnType() == Void.TYPE) + { + _log.add(Thread.currentThread() + ": " + method.getName() + "() returned"); + } + else + { + _log.add(Thread.currentThread() + ": " + method.getName() + "() returned " + result); + } + } + + public Object getProxy(Class... c) + { + return Proxy.newProxyInstance(_target.getClass().getClassLoader(), c, this); + } + + public int getBufferSize() { + return _log.size(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java new file mode 100644 index 0000000000..ee5f9d5e88 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.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.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Properties; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.MapConfiguration; +import org.apache.qpid.server.management.ManagedObjectRegistry; +import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.plugins.PluginManager; +import org.apache.qpid.server.registry.ApplicationRegistry; +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.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.plugins.AllowAll; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +public class NullApplicationRegistry extends ApplicationRegistry +{ + public NullApplicationRegistry() + { + super(new MapConfiguration(new HashMap())); + } + + public void initialise() throws Exception + { + _logger.info("Initialising NullApplicationRegistry"); + + _configuration.addProperty("store.class", "org.apache.qpid.server.store.MemoryMessageStore"); + + Properties users = new Properties(); + + users.put("guest", "guest"); + + _databaseManager = new PropertiesPrincipalDatabaseManager("default", users); + + _accessManager = new AllowAll(); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); + + _managedObjectRegistry = new NoopManagedObjectRegistry(); + _virtualHostRegistry = new VirtualHostRegistry(); + VirtualHost dummyHost = new VirtualHost("test", getConfiguration()); + _virtualHostRegistry.registerVirtualHost(dummyHost); + _virtualHostRegistry.setDefaultVirtualHostName("test"); + _pluginManager = new PluginManager(""); + _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes + + } + + public Collection getVirtualHostNames() + { + String[] hosts = {"test"}; + return Arrays.asList(hosts); + } +} + + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java new file mode 100644 index 0000000000..85d804457e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.virtualhost; + +import java.io.IOException; + +import org.apache.qpid.server.management.MBeanAttribute; + +/** + * The management interface exposed to allow management of an Exchange. + * @version 0.1 + */ +public interface ManagedVirtualHost +{ + static final String TYPE = "VirtualHost"; + + /** + * Returns the name of the managed virtualHost. + * @return the name of the exchange. + * @throws java.io.IOException + */ + @MBeanAttribute(name="Name", description= TYPE + " Name") + String getName() throws IOException; + + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java new file mode 100644 index 0000000000..9229863c35 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -0,0 +1,339 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.virtualhost; + +import javax.management.NotCompliantMBeanException; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.log4j.Logger; +import org.apache.qpid.server.AMQBrokerManagerMBean; +import org.apache.qpid.server.connection.ConnectionRegistry; +import org.apache.qpid.server.connection.IConnectionRegistry; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.ACLManager; +import org.apache.qpid.server.security.access.Accessable; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.exchange.DefaultExchangeFactory; +import org.apache.qpid.server.exchange.DefaultExchangeRegistry; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.queue.DefaultQueueRegistry; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.AMQException; + +import java.util.Timer; +import java.util.TimerTask; + +public class VirtualHost implements Accessable +{ + private static final Logger _logger = Logger.getLogger(VirtualHost.class); + + + private final String _name; + + private ConnectionRegistry _connectionRegistry; + + private QueueRegistry _queueRegistry; + + private ExchangeRegistry _exchangeRegistry; + + private ExchangeFactory _exchangeFactory; + + private MessageStore _messageStore; + + protected VirtualHostMBean _virtualHostMBean; + + private AMQBrokerManagerMBean _brokerMBean; + + private AuthenticationManager _authenticationManager; + + private ACLPlugin _accessManager; + + private final Timer _houseKeepingTimer; + + private static final long DEFAULT_HOUSEKEEPING_PERIOD = 30000L; + + + public void setAccessableName(String name) + { + _logger.warn("Setting Accessable Name for VirualHost is not allowed. (" + + name + ") ignored remains :" + getAccessableName()); + } + + public String getAccessableName() + { + return _name; + } + + public IConnectionRegistry getConnectionRegistry() + { + return _connectionRegistry; + } + + /** + * 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 class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost + { + public VirtualHostMBean() throws NotCompliantMBeanException + { + super(ManagedVirtualHost.class, "VirtualHost"); + } + + public String getObjectInstanceName() + { + return _name.toString(); + } + + public String getName() + { + return _name.toString(); + } + + public VirtualHost getVirtualHost() + { + return VirtualHost.this; + } + + + } // End of MBean class + + /** + * Used for testing only + * @param name + * @param store + * @throws Exception + */ + public VirtualHost(String name, MessageStore store) throws Exception + { + this(name, new PropertiesConfiguration(), store); + } + + /** + * Normal Constructor + * @param name + * @param hostConfig + * @throws Exception + */ + public VirtualHost(String name, Configuration hostConfig) throws Exception + { + this(name, hostConfig, null); + } + + public VirtualHost(String name, Configuration hostConfig, MessageStore store) throws Exception + { + if (name == null || name.length() == 0) + { + throw new IllegalArgumentException("Illegal name (" + name + ") for virtualhost."); + } + + _name = name; + + _virtualHostMBean = new VirtualHostMBean(); + + _connectionRegistry = new ConnectionRegistry(this); + + _houseKeepingTimer = new Timer("Queue-housekeeping-"+name, true); + _queueRegistry = new DefaultQueueRegistry(this); + _exchangeFactory = new DefaultExchangeFactory(this); + _exchangeFactory.initialise(hostConfig); + _exchangeRegistry = new DefaultExchangeRegistry(this); + + if (store != null) + { + _messageStore = store; + } + else + { + if (hostConfig == null) + { + throw new IllegalAccessException("HostConfig and MessageStore cannot be null"); + } + initialiseMessageStore(hostConfig); + } + + _exchangeRegistry.initialise(); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(name, hostConfig); + + _accessManager = ACLManager.loadACLManager(name, hostConfig); + + _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); + _brokerMBean.register(); + initialiseHouseKeeping(hostConfig); + } + + private void initialiseHouseKeeping(final Configuration hostConfig) + { + + long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD); + + /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */ + if(period != 0L) + { + class RemoveExpiredMessagesTask extends TimerTask + { + public void run() + { + for(AMQQueue q : _queueRegistry.getQueues()) + { + + try + { + q.removeExpiredIfNoSubscribers(); + } + catch (AMQException e) + { + _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e); + throw new RuntimeException(e); + } + } + } + } + + _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(), + period/2, + period); + } + } + + private void initialiseMessageStore(Configuration config) throws Exception + { + String messageStoreClass = config.getString("store.class"); + + Class clazz = Class.forName(messageStoreClass); + Object o = clazz.newInstance(); + + if (!(o instanceof MessageStore)) + { + throw new ClassCastException("Message store class must implement " + MessageStore.class + ". Class " + clazz + + " does not."); + } + _messageStore = (MessageStore) o; + _messageStore.configure(this, "store", config); + } + + + public T getConfiguredObject(Class instanceType, Configuration config) + { + T instance; + try + { + instance = instanceType.newInstance(); + } + catch (Exception e) + { + _logger.error("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor"); + throw new IllegalArgumentException("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor", e); + } + Configurator.configure(instance); + + return instance; + } + + + public String getName() + { + return _name; + } + + public QueueRegistry getQueueRegistry() + { + return _queueRegistry; + } + + public ExchangeRegistry getExchangeRegistry() + { + return _exchangeRegistry; + } + + public ExchangeFactory getExchangeFactory() + { + return _exchangeFactory; + } + + public ApplicationRegistry getApplicationRegistry() + { + throw new UnsupportedOperationException(); + } + + public MessageStore getMessageStore() + { + return _messageStore; + } + + public AuthenticationManager getAuthenticationManager() + { + return _authenticationManager; + } + + public ACLPlugin getAccessManager() + { + return _accessManager; + } + + public void close() throws Exception + { + + //Stop Connections + _connectionRegistry.close(); + + //Stop the Queues processing + if (_queueRegistry != null) + { + for (AMQQueue queue : _queueRegistry.getQueues()) + { + queue.stop(); + } + } + + //Stop Housekeeping + if (_houseKeepingTimer != null) + { + _houseKeepingTimer.cancel(); + } + + //Close MessageStore + if (_messageStore != null) + { + _messageStore.close(); + } + } + + public ManagedObject getBrokerMBean() + { + return _brokerMBean; + } + + public ManagedObject getManagedObject() + { + return _virtualHostMBean; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java new file mode 100644 index 0000000000..27917fac8a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class VirtualHostRegistry +{ + private final Map _registry = new ConcurrentHashMap(); + + + private String _defaultVirtualHostName; + + public synchronized void registerVirtualHost(VirtualHost host) throws Exception + { + if(_registry.containsKey(host.getName())) + { + throw new Exception("Virtual Host with name " + host.getName() + " already registered."); + } + _registry.put(host.getName(),host); + } + + public VirtualHost getVirtualHost(String name) + { + if(name == null || name.trim().length() == 0 ) + { + name = getDefaultVirtualHostName(); + } + + return _registry.get(name); + } + + private String getDefaultVirtualHostName() + { + return _defaultVirtualHostName; + } + + public void setDefaultVirtualHostName(String defaultVirtualHostName) + { + _defaultVirtualHostName = defaultVirtualHostName; + } + + + public Collection getVirtualHosts() + { + return new ArrayList(_registry.values()); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java new file mode 100644 index 0000000000..faa7b85d58 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java @@ -0,0 +1,652 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.OptionBuilder; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.configuration.Configuration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.tools.messagestore.commands.Clear; +import org.apache.qpid.tools.messagestore.commands.Command; +import org.apache.qpid.tools.messagestore.commands.Copy; +import org.apache.qpid.tools.messagestore.commands.Dump; +import org.apache.qpid.tools.messagestore.commands.Help; +import org.apache.qpid.tools.messagestore.commands.List; +import org.apache.qpid.tools.messagestore.commands.Load; +import org.apache.qpid.tools.messagestore.commands.Quit; +import org.apache.qpid.tools.messagestore.commands.Select; +import org.apache.qpid.tools.messagestore.commands.Show; +import org.apache.qpid.tools.messagestore.commands.Move; +import org.apache.qpid.tools.messagestore.commands.Purge; +import org.apache.qpid.tools.utils.CommandParser; +import org.apache.qpid.tools.utils.Console; +import org.apache.qpid.tools.utils.SimpleCommandParser; +import org.apache.qpid.tools.utils.SimpleConsole; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * MessageStoreTool. + */ +public class MessageStoreTool +{ + /** Text outputted at the start of each console.*/ + private static final String BOILER_PLATE = "MessageStoreTool - for examining Persistent Qpid Broker MessageStore instances"; + + /** I/O Wrapper. */ + protected Console _console; + + /** Batch mode flag. */ + protected boolean _batchMode; + + /** Internal State object. */ + private State _state = new State(); + + private HashMap _commands = new HashMap(); + + /** SLF4J Logger. */ + private static Logger _devlog = LoggerFactory.getLogger(MessageStoreTool.class); + + /** Loaded configuration file. */ + private Configuration _config; + + /** Control used for main run loop. */ + private boolean _running = true; + private boolean _initialised = false; + + //---------------------------------------------------------------------------------------------------/ + + public static void main(String[] args) throws Configuration.InitException + { + + MessageStoreTool tool = new MessageStoreTool(args); + + tool.start(); + } + + + public MessageStoreTool(String[] args) throws Configuration.InitException + { + this(args, System.in, System.out); + } + + public MessageStoreTool(String[] args, InputStream in, OutputStream out) throws Configuration.InitException + { + BufferedReader consoleReader = new BufferedReader(new InputStreamReader(in)); + BufferedWriter consoleWriter = new BufferedWriter(new OutputStreamWriter(out)); + + Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook(this))); + _batchMode = false; + + _console = new SimpleConsole(consoleWriter, consoleReader); + + _config = new Configuration(); + + setOptions(); + _config.processCommandline(args); + } + + + private void setOptions() + { + Option help = new Option("h", "help", false, "print this message"); + Option version = new Option("v", "version", false, "print the version information and exit"); + Option configFile = + OptionBuilder.withArgName("file").hasArg() + .withDescription("use given configuration file By " + + "default looks for a file named " + + Configuration.DEFAULT_CONFIG_FILE + " in " + Configuration.QPID_HOME) + .withLongOpt("config") + .create("c"); + + _config.setOption(help); + _config.setOption(version); + _config.setOption(configFile); + } + + public State getState() + { + return _state; + } + + public Map getCommands() + { + return _commands; + } + + public void setConfigurationFile(String configfile) throws Configuration.InitException + { + _config.loadConfig(new File(configfile)); + setup(); + } + + public Console getConsole() + { + return _console; + } + + public void setConsole(Console console) + { + _console = console; + } + + /** + * Simple ShutdownHook to cleanly shutdown the databases + */ + class ShutdownHook implements Runnable + { + MessageStoreTool _tool; + + ShutdownHook(MessageStoreTool messageStoreTool) + { + _tool = messageStoreTool; + } + + public void run() + { + _tool.quit(); + } + } + + public void quit() + { + _running = false; + + if (_initialised) + { + ApplicationRegistry.remove(1); + } + + _console.println("...exiting"); + + _console.close(); + } + + public void setBatchMode(boolean batchmode) + { + _batchMode = batchmode; + } + + /** + * Main loop + */ + protected void start() + { + setup(); + + if (!_initialised) + { + System.exit(1); + } + + _console.println(""); + + _console.println(BOILER_PLATE); + + runCLI(); + } + + private void setup() + { + loadDefaultVirtualHosts(); + + loadCommands(); + + _state.clearAll(); + } + + private void loadCommands() + { + _commands.clear(); + //todo Dynamically load the classes that exis in com.redhat.etp.qpid.commands + _commands.put("close", new Clear(this)); + _commands.put("copy", new Copy(this)); + _commands.put("dump", new Dump(this)); + _commands.put("help", new Help(this)); + _commands.put("list", new List(this)); + _commands.put("load", new Load(this)); + _commands.put("move", new Move(this)); + _commands.put("purge", new Purge(this)); + _commands.put("quit", new Quit(this)); + _commands.put("select", new Select(this)); + _commands.put("show", new Show(this)); + } + + private void loadDefaultVirtualHosts() + { + final File configFile = _config.getConfigFile(); + + loadVirtualHosts(configFile); + } + + private void loadVirtualHosts(File configFile) + { + + if (!configFile.exists()) + { + _devlog.error("Config file not found:" + configFile.getAbsolutePath()); + return; + } + else + { + _devlog.debug("using config file :" + configFile.getAbsolutePath()); + } + + try + { + ConfigurationFileApplicationRegistry registry = new ConfigurationFileApplicationRegistry(configFile); + + ApplicationRegistry.remove(1); + + ApplicationRegistry.initialise(registry); + + checkMessageStores(); + _initialised = true; + } + catch (ConfigurationException e) + { + _console.println("Unable to load configuration due to configuration error: " + e.getMessage()); + e.printStackTrace(); + } + catch (Exception e) + { + _console.println("Unable to load configuration due to: " + e.getMessage()); + e.printStackTrace(); + } + + + } + + private void checkMessageStores() + { + Collection vhosts = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts(); + + boolean warning = false; + for (VirtualHost vhost : vhosts) + { + if (vhost.getMessageStore() instanceof MemoryMessageStore) + { + _console.println("WARNING: Virtualhost '" + vhost.getName() + "' is using a MemoryMessageStore. " + + "Changes will not persist."); + warning = true; + } + } + + if (warning) + { + _console.println(""); + _console.println("Please ensure you are using the correct config file currently using '" + + _config.getConfigFile().getAbsolutePath() + "'"); + _console.println("New config file can be specifed by 'load ' or -c on the commandline."); + _console.println(""); + } + } + + private void runCLI() + { + while (_running) + { + if (!_batchMode) + { + printPrompt(); + } + + String[] args = _console.readCommand(); + + while (args != null) + { + exec(args); + + if (_running) + { + if (!_batchMode) + { + printPrompt(); + } + + args = _console.readCommand(); + } + } + } + } + + private void printPrompt() + { + _console.print(prompt()); + } + + + /** + * Execute a script (batch mode). + * + * @param script The file script + */ + protected void runScripts(String script) + { + //Store Current State + boolean oldBatch = _batchMode; + CommandParser oldParser = _console.getCommandParser(); + setBatchMode(true); + + try + { + _devlog.debug("Running script '" + script + "'"); + + _console.setCommandParser(new SimpleCommandParser(new BufferedReader(new FileReader(script)))); + + start(); + } + catch (java.io.FileNotFoundException e) + { + _devlog.error("Script not found: '" + script + "' due to:" + e.getMessage()); + } + + //Restore previous state + _console.setCommandParser(oldParser); + setBatchMode(oldBatch); + } + + public String prompt() + { + String state = _state.toString(); + if (state != null && state.length() != 0) + { + return state + ":bdb$ "; + } + else + { + return "bdb$ "; + } + } + + /** + * Execute the command. + * + * @param args [command, arg0, arg1...]. + */ + protected void exec(String[] args) + { + // Comment lines start with a # + if (args.length == 0 || args[0].startsWith("#")) + { + return; + } + + final String command = args[0]; + + Command cmd = _commands.get(command); + + if (cmd == null) + { + _console.println("Command not understood: " + command); + } + else + { + cmd.execute(args); + } + } + + + /** + * Displays usage info. + */ + protected static void help() + { + System.out.println(BOILER_PLATE); + System.out.println("Usage: java " + MessageStoreTool.class + " [Options]"); + System.out.println(" [-c ] : Defaults to \"$QPID_HOME/etc/config.xml\""); + } + + + /** + * This class is used to store the current state of the tool. + * + * This is then interrogated by the various commands to augment their behaviour. + * + * + */ + public class State + { + private VirtualHost _vhost = null; + private AMQQueue _queue = null; + private Exchange _exchange = null; + private java.util.List _msgids = null; + + public State() + { + } + + public void setQueue(AMQQueue queue) + { + _queue = queue; + } + + public AMQQueue getQueue() + { + return _queue; + } + + public void setVhost(VirtualHost vhost) + { + _vhost = vhost; + } + + public VirtualHost getVhost() + { + return _vhost; + } + + public Exchange getExchange() + { + return _exchange; + } + + public void setExchange(Exchange exchange) + { + _exchange = exchange; + } + + public String toString() + { + StringBuilder status = new StringBuilder(); + + if (_vhost != null) + { + status.append(_vhost.getName()); + + if (_exchange != null) + { + status.append("["); + status.append(_exchange.getName()); + status.append("]"); + + if (_queue != null) + { + status.append("->'"); + status.append(_queue.getName()); + status.append("'"); + + if (_msgids != null) + { + status.append(printMessages()); + } + } + } + } + + return status.toString(); + } + + + public String printMessages() + { + StringBuilder sb = new StringBuilder(); + + Long previous = null; + + Long start = null; + for (Long id : _msgids) + { + if (previous != null) + { + if (id == previous + 1) + { + if (start == null) + { + start = previous; + } + } + else + { + if (start != null) + { + sb.append(","); + sb.append(start); + sb.append("-"); + sb.append(id); + start = null; + } + else + { + sb.append(","); + sb.append(previous); + } + } + } + + previous = id; + } + + if (start != null) + { + sb.append(","); + sb.append(start); + sb.append("-"); + sb.append(_msgids.get(_msgids.size() - 1)); + } + else + { + sb.append(","); + sb.append(previous); + } + + // surround list in () + sb.replace(0, 1, "("); + sb.append(")"); + return sb.toString(); + } + + public void clearAll() + { + _vhost = null; + clearExchange(); + } + + public void clearExchange() + { + _exchange = null; + clearQueue(); + } + + public void clearQueue() + { + _queue = null; + clearMessages(); + } + + public void clearMessages() + { + _msgids = null; + } + + /** + * A common location to provide parsing of the message id string + * utilised by a number of the commands. + * The String is comma separated list of ids that can be individual ids + * or a range (4-10) + * + * @param msgString string of msg ids to parse 1,2,4-10 + */ + public void setMessages(String msgString) + { + StringTokenizer tok = new StringTokenizer(msgString, ","); + + if (tok.hasMoreTokens()) + { + _msgids = new LinkedList(); + } + + while (tok.hasMoreTokens()) + { + String next = tok.nextToken(); + if (next.contains("-")) + { + Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); + Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); + + if (end >= start) + { + for (long l = start; l <= end; l++) + { + _msgids.add(l); + } + } + } + else + { + _msgids.add(Long.parseLong(next)); + } + } + + } + + public void setMessages(java.util.List msgids) + { + _msgids = msgids; + } + + public java.util.List getMessages() + { + return _msgids; + } + }//Class State + +}//Class MessageStoreTool diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java new file mode 100644 index 0000000000..5444197cb4 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +public abstract class AbstractCommand implements Command +{ + protected Console _console; + protected MessageStoreTool _tool; + + public AbstractCommand(MessageStoreTool tool) + { + _console = tool.getConsole(); + _tool = tool; + } + + public void setOutput(Console out) + { + _console = out; + } + + protected void commandError(String message, String[] args) + { + _console.print(getCommand() + " : " + message); + + if (args != null) + { + for (int i = 1; i < args.length; i++) + { + _console.print(args[i]); + } + } + _console.println(""); + _console.println(help()); + } + + + public abstract String help(); + + public abstract String usage(); + + public abstract String getCommand(); + + + public abstract void execute(String... args); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java new file mode 100644 index 0000000000..b0006b3fe6 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +public class Clear extends AbstractCommand +{ + public Clear(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Clears any selection."; + } + + public String usage() + { + return "clear [ all | virtualhost | exchange | queue | msgs ]"; + } + + public String getCommand() + { + return "clear"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + if (args.length < 1) + { + doClose("all"); + } + else + { + doClose(args[1]); + } + } + + private void doClose(String type) + { + if (type.equals("virtualhost") + || type.equals("all")) + { + _tool.getState().clearAll(); + } + + if (type.equals("exchange")) + { + _tool.getState().clearExchange(); + } + + if (type.equals("queue")) + { + _tool.getState().clearQueue(); + } + + if (type.equals("msgs")) + { + _tool.getState().clearMessages(); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java new file mode 100644 index 0000000000..bfa775a34a --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.utils.Console; + +public interface Command +{ + public void setOutput(Console out); + + public String help(); + + public abstract String usage(); + + String getCommand(); + + public void execute(String... args); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java new file mode 100644 index 0000000000..0869d9a497 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.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.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.server.queue.AMQQueue; + +public class Copy extends Move +{ + public Copy(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Copy messages between queues.";/*\n" + + "The currently selected message set will be copied to the specifed queue.\n" + + "Alternatively the values can be provided on the command line."; */ + } + + public String usage() + { + return "copy to= [from=] [msgids=]"; + } + + public String getCommand() + { + return "copy"; + } + + protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) + { + fromQueue.copyMessagesToAnotherQueue(start, end, toQueue.getName().toString(), _storeContext); + } + +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java new file mode 100644 index 0000000000..731f6140f9 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.commons.codec.binary.Hex; +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryImpl; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +import java.io.UnsupportedEncodingException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class Dump extends Show +{ + private static final int LINE_SIZE = 8; + private static final String DEFAULT_ENCODING = "utf-8"; + private static final boolean SPACE_BYTES = true; + private static final String BYTE_SPACER = " "; + private static final String NON_PRINTING_ASCII_CHAR = "?"; + + protected boolean _content = true; + + public Dump(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Dump selected message content. Default: show=content"; + } + + public String usage() + { + return getCommand() + " [show=[all],[msgheaders],[_amqHeaders],[routing],[content]] [id=]"; + } + + public String getCommand() + { + return "dump"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + + if (args.length >= 2) + { + for (String arg : args) + { + if (arg.startsWith("show=")) + { + _content = arg.contains("content") || arg.contains("all"); + } + } + + parseArgs(args); + } + + performShow(); + } + + + protected List createMessageData(java.util.List msgids, List messages, boolean showHeaders, boolean showRouting, + boolean showMessageHeaders) + { + + List display = new LinkedList(); + + List hex = new LinkedList(); + List ascii = new LinkedList(); + display.add(hex); + display.add(ascii); + + for (QueueEntry entry : messages) + { + AMQMessage msg = entry.getMessage(); + if (!includeMsg(msg, msgids)) + { + continue; + } + + //Add divider between messages + hex.add(Console.ROW_DIVIDER); + ascii.add(Console.ROW_DIVIDER); + + // Show general message information + hex.add(Show.Columns.ID.name()); + ascii.add(msg.getMessageId().toString()); + + hex.add(Console.ROW_DIVIDER); + ascii.add(Console.ROW_DIVIDER); + + if (showRouting) + { + addShowInformation(hex, ascii, msg, "Routing Details", true, false, false); + } + if (showHeaders) + { + addShowInformation(hex, ascii, msg, "Headers", false, true, false); + } + if (showMessageHeaders) + { + addShowInformation(hex, ascii, msg, null, false, false, true); + } + + // Add Content Body seciont + hex.add("Content Body"); + ascii.add(""); + hex.add(Console.ROW_DIVIDER); + ascii.add(Console.ROW_DIVIDER); + + Iterator bodies = msg.getContentBodyIterator(); + if (bodies.hasNext()) + { + + hex.add("Hex"); + hex.add(Console.ROW_DIVIDER); + + + ascii.add("ASCII"); + ascii.add(Console.ROW_DIVIDER); + + while (bodies.hasNext()) + { + ContentChunk chunk = (ContentChunk) bodies.next(); + + //Duplicate so we don't destroy original data :) + ByteBuffer hexBuffer = chunk.getData().duplicate(); + + ByteBuffer charBuffer = hexBuffer.duplicate(); + + Hex hexencoder = new Hex(); + + while (hexBuffer.hasRemaining()) + { + byte[] line = new byte[LINE_SIZE]; + + int bufsize = hexBuffer.remaining(); + if (bufsize < LINE_SIZE) + { + hexBuffer.get(line, 0, bufsize); + } + else + { + bufsize = line.length; + hexBuffer.get(line); + } + + byte[] encoded = hexencoder.encode(line); + + try + { + String encStr = new String(encoded, 0, bufsize * 2, DEFAULT_ENCODING); + String hexLine = ""; + + int strKength = encStr.length(); + for (int c = 0; c < strKength; c++) + { + hexLine += encStr.charAt(c); + + if (c % 2 == 1 && SPACE_BYTES) + { + hexLine += BYTE_SPACER; + } + } + + hex.add(hexLine); + } + catch (UnsupportedEncodingException e) + { + _console.println(e.getMessage()); + return null; + } + } + + while (charBuffer.hasRemaining()) + { + String asciiLine = ""; + + for (int pos = 0; pos < LINE_SIZE; pos++) + { + if (charBuffer.hasRemaining()) + { + byte ch = charBuffer.get(); + + if (isPrintable(ch)) + { + asciiLine += (char) ch; + } + else + { + asciiLine += NON_PRINTING_ASCII_CHAR; + } + + if (SPACE_BYTES) + { + asciiLine += BYTE_SPACER; + } + } + else + { + break; + } + } + + ascii.add(asciiLine); + } + } + } + else + { + List result = new LinkedList(); + + display.add(result); + result.add("No ContentBodies"); + } + } + + // if hex is empty then we have no data to display + if (hex.size() == 0) + { + return null; + } + + return display; + } + + private void addShowInformation(List column1, List column2, AMQMessage msg, + String title, boolean routing, boolean headers, boolean messageHeaders) + { + List single = new LinkedList(); + single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE)); + + List routingData = super.createMessageData(null, single, headers, routing, messageHeaders); + + //Reformat data + if (title != null) + { + column1.add(title); + column2.add(""); + column1.add(Console.ROW_DIVIDER); + column2.add(Console.ROW_DIVIDER); + } + + // look at all columns in the routing Data + for (List item : routingData) + { + // the item should be: + // Title + // *divider + // value + // otherwise we can't reason about the correct value + if (item.size() == 3) + { + //Filter out the columns we are not interested in. + + String columnName = item.get(0).toString(); + + if (!(columnName.equals(Show.Columns.ID.name()) + || columnName.equals(Show.Columns.Size.name()))) + { + column1.add(columnName); + column2.add(item.get(2).toString()); + } + } + } + column1.add(Console.ROW_DIVIDER); + column2.add(Console.ROW_DIVIDER); + } + + private boolean isPrintable(byte c) + { + return c > 31 && c < 127; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java new file mode 100644 index 0000000000..0f9546541b --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +import java.util.LinkedList; +import java.util.Map; + +public class Help extends AbstractCommand +{ + public Help(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Provides detailed help on commands."; + } + + public String getCommand() + { + return "help"; + } + + public String usage() + { + return "help []"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + if (args.length > 1) + { + Command command = _tool.getCommands().get(args[1]); + if (command != null) + { + _console.println(command.help()); + _console.println("Usage:" + command.usage()); + } + else + { + commandError("Command not found: ", args); + } + } + else + { + java.util.List data = new LinkedList(); + + java.util.List commandName = new LinkedList(); + java.util.List commandDescription = new LinkedList(); + + data.add(commandName); + data.add(commandDescription); + + //Set up Headers + commandName.add("Command"); + commandDescription.add("Description"); + + commandName.add(Console.ROW_DIVIDER); + commandDescription.add(Console.ROW_DIVIDER); + + //Add current Commands with descriptions + Map commands = _tool.getCommands(); + + for (Command command : commands.values()) + { + commandName.add(command.getCommand()); + commandDescription.add(command.help()); + } + + _console.printMap("Available Commands", data); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java new file mode 100644 index 0000000000..df8b59ec19 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java @@ -0,0 +1,314 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +import java.util.Collection; +import java.util.LinkedList; + +public class List extends AbstractCommand +{ + + public List(MessageStoreTool tool) + { + super(tool); + } + + public void setOutput(Console out) + { + _console = out; + } + + public String help() + { + return "list available items."; + } + + public String usage() + { + return "list queues [] | exchanges | bindings [] | all"; + } + + public String getCommand() + { + return "list"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + if (args.length > 1) + { + if ((args[1].equals("exchanges")) + || (args[1].equals("queues")) + || (args[1].equals("bindings")) + || (args[1].equals("all"))) + { + if (args.length == 2) + { + doList(args[1]); + } + else if (args.length == 3) + { + doList(args[1], args[2]); + } + } + else + { + commandError("Unknown options. ", args); + } + } + else if (args.length < 2) + { + doList("all"); + } + else + { + doList(args[1]); + } + } + + private void doList(String... listItem) + { + if (_tool.getState().getVhost() == null) + { + _console.println("No Virtualhost open. Open a Virtualhost first."); + listVirtualHosts(); + return; + } + + VirtualHost vhost = _tool.getState().getVhost(); + + java.util.List data = null; + + if (listItem[0].equals("queues")) + { + if (listItem.length > 1) + { + data = listQueues(vhost, new AMQShortString(listItem[1])); + } + else + { + Exchange exchange = _tool.getState().getExchange(); + data = listQueues(vhost, exchange); + } + } + + if (listItem[0].equals("exchanges")) + { + data = listExchanges(vhost); + } + + if (listItem[0].equals("bindings")) + { + + if (listItem.length > 1) + { + data = listBindings(vhost, new AMQShortString(listItem[1])); + } + else + { + Exchange exchange = _tool.getState().getExchange(); + + data = listBindings(vhost, exchange); + } + } + + if (data != null) + { + if (data.size() == 1) + { + _console.println("No '" + listItem[0] + "' to display,"); + } + else + { + _console.displayList(true, data.toArray(new String[0])); + } + } + + + if (listItem[0].equals("all")) + { + + boolean displayed = false; + Exchange exchange = _tool.getState().getExchange(); + + //Do the display here for each one so that they are pretty printed + data = listQueues(vhost, exchange); + if (data != null) + { + displayed = true; + _console.displayList(true, data.toArray(new String[0])); + } + + if (exchange == null) + { + data = listExchanges(vhost); + if (data != null) + { + displayed = true; + _console.displayList(true, data.toArray(new String[0])); + } + } + + data = listBindings(vhost, exchange); + if (data != null) + { + displayed = true; + _console.displayList(true, data.toArray(new String[0])); + } + + if (!displayed) + { + _console.println("Nothing to list"); + } + } + } + + private void listVirtualHosts() + { + Collection vhosts = ApplicationRegistry.getInstance() + .getVirtualHostRegistry().getVirtualHosts(); + + String[] data = new String[vhosts.size() + 1]; + + data[0] = "Available VirtualHosts"; + + int index = 1; + for (VirtualHost vhost : vhosts) + { + data[index] = vhost.getName(); + index++; + } + + _console.displayList(true, data); + } + + private java.util.List listBindings(VirtualHost vhost, AMQShortString exchangeName) + { + return listBindings(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); + } + + private java.util.List listBindings(VirtualHost vhost, Exchange exchange) + { + Collection queues = vhost.getQueueRegistry().getQueueNames(); + + if (queues == null || queues.size() == 0) + { + return null; + } + + java.util.List data = new LinkedList(); + + data.add("Current Bindings"); + + for (AMQShortString queue : queues) + { + if (exchange != null) + { + if (exchange.isBound(queue)) + { + data.add(queue.toString()); + } + } + else + { + data.add(queue.toString()); + } + } + + return data; + } + + private java.util.List listExchanges(VirtualHost vhost) + { + Collection queues = vhost.getExchangeRegistry().getExchangeNames(); + + if (queues == null || queues.size() == 0) + { + return null; + } + + java.util.List data = new LinkedList(); + + data.add("Available Exchanges"); + + for (AMQShortString queue : queues) + { + data.add(queue.toString()); + } + + return data; + } + + private java.util.List listQueues(VirtualHost vhost, AMQShortString exchangeName) + { + return listQueues(vhost, vhost.getExchangeRegistry().getExchange(exchangeName)); + } + + private java.util.List listQueues(VirtualHost vhost, Exchange exchange) + { + Collection queues = vhost.getQueueRegistry().getQueues(); + + if (queues == null || queues.size() == 0) + { + return null; + } + + java.util.List data = new LinkedList(); + + data.add("Available Queues"); + + for (AMQQueue queue : queues) + { + if (exchange != null) + { + if (exchange.isBound(queue)) + { + data.add(queue.getName().toString()); + } + } + else + { + data.add(queue.getName().toString()); + } + } + + if (exchange != null) + { + if (queues.size() == 1) + { + return null; + } + } + + return data; + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java new file mode 100644 index 0000000000..244a311c30 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.configuration.Configuration; +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +public class Load extends AbstractCommand +{ + public Load(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Loads specified broker configuration file."; + } + + public String usage() + { + return "load "; + } + + public String getCommand() + { + return "load"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + if (args.length > 2) + { + _console.print("load " + args[1] + ": additional options not understood:"); + for (int i = 2; i < args.length; i++) + { + _console.print(args[i] + " "); + } + _console.println(""); + } + else if (args.length < 2) + { + _console.println("Enter Configuration file."); + String input = _console.readln(); + if (input != null) + { + doLoad(input); + } + else + { + _console.println("Did not recognise config file."); + } + } + else + { + doLoad(args[1]); + } + } + + private void doLoad(String configfile) + { + _console.println("Loading Configuration:" + configfile); + + try + { + _tool.setConfigurationFile(configfile); + } + catch (Configuration.InitException e) + { + _console.println("Unable to open config file due to: '" + e.getMessage() + "'"); + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java new file mode 100644 index 0000000000..a8dd58ca83 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.queue.QueueEntryImpl; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +import java.util.LinkedList; +import java.util.List; + +public class Move extends AbstractCommand +{ + + /** + * Since the Coopy command is not associated with a real channel we can safely create our own store context + * for use in the few methods that require one. + */ + protected StoreContext _storeContext = new StoreContext(); + + public Move(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Move messages between queues.";/*\n" + + "The currently selected message set will be moved to the specifed queue.\n" + + "Alternatively the values can be provided on the command line.";*/ + } + + public String usage() + { + return "move to= [from=] [msgids=]"; + } + + public String getCommand() + { + return "move"; + } + + public void execute(String... args) + { + AMQQueue toQueue = null; + AMQQueue fromQueue = _tool.getState().getQueue(); + java.util.List msgids = _tool.getState().getMessages(); + + if (args.length >= 2) + { + for (String arg : args) + { + if (arg.startsWith("to=")) + { + String queueName = arg.substring(arg.indexOf("=") + 1); + toQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + } + + if (arg.startsWith("from=")) + { + String queueName = arg.substring(arg.indexOf("=") + 1); + fromQueue = _tool.getState().getVhost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + } + + if (arg.startsWith("msgids=")) + { + String msgidStr = arg.substring(arg.indexOf("=") + 1); + + // Record the current message selection + java.util.List currentIDs = _tool.getState().getMessages(); + + // Use the ToolState class to perform the messasge parsing + _tool.getState().setMessages(msgidStr); + msgids = _tool.getState().getMessages(); + + // Reset the original selection of messages + _tool.getState().setMessages(currentIDs); + } + } + } + + if (!checkRequirements(fromQueue, toQueue, msgids)) + { + return; + } + + processIDs(fromQueue, toQueue, msgids); + } + + private void processIDs(AMQQueue fromQueue, AMQQueue toQueue, java.util.List msgids) + { + Long previous = null; + Long start = null; + + if (msgids == null) + { + msgids = allMessageIDs(fromQueue); + } + + if (msgids == null || msgids.size() == 0) + { + _console.println("No Messages to move."); + return; + } + + for (long id : msgids) + { + if (previous != null) + { + if (id == previous + 1) + { + if (start == null) + { + start = previous; + } + } + else + { + if (start != null) + { + //move a range of ids + doCommand(fromQueue, start, id, toQueue); + start = null; + } + else + { + //move a single id + doCommand(fromQueue, id, id, toQueue); + } + } + } + + previous = id; + } + + if (start != null) + { + //move a range of ids + doCommand(fromQueue, start, previous, toQueue); + } + } + + private List allMessageIDs(AMQQueue fromQueue) + { + List ids = new LinkedList(); + + if (fromQueue != null) + { + List messages = fromQueue.getMessagesOnTheQueue(); + if (messages != null) + { + for (QueueEntry msg : messages) + { + ids.add(msg.getMessage().getMessageId()); + } + } + } + + return ids; + } + + protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, List msgids) + { + if (toQueue == null) + { + _console.println("Destination queue not specifed."); + _console.println(usage()); + return false; + } + + if (fromQueue == null) + { + _console.println("Source queue not specifed."); + _console.println(usage()); + return false; + } + + return true; + } + + protected void doCommand(AMQQueue fromQueue, long start, long id, AMQQueue toQueue) + { + fromQueue.moveMessagesToAnotherQueue(start, id, toQueue.getName().toString(), _storeContext); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java new file mode 100644 index 0000000000..5e99997863 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.server.queue.AMQQueue; + +public class Purge extends Move +{ + public Purge(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Purge messages from a queue.\n" + + "The currently selected message set will be purged from the specifed queue.\n" + + "Alternatively the values can be provided on the command line."; + } + + public String usage() + { + return "purge from= [msgids=]"; + } + + public String getCommand() + { + return "purge"; + } + + + protected boolean checkRequirements(AMQQueue fromQueue, AMQQueue toQueue, java.util.List msgids) + { + if (fromQueue == null) + { + _console.println("Source queue not specifed."); + _console.println(usage()); + return false; + } + + return true; + } + + protected void doCommand(AMQQueue fromQueue, long start, long end, AMQQueue toQueue) + { + fromQueue.removeMessagesFromQueue(start, end, _storeContext); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java new file mode 100644 index 0000000000..a81bc07c38 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +public class Quit extends AbstractCommand +{ + public Quit(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Quit the tool."; + } + + public String usage() + { + return "quit"; + } + + public String getCommand() + { + return "quit"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals("quit"); + + _tool.quit(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java new file mode 100644 index 0000000000..ff59568374 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.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.tools.messagestore.commands; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +import java.util.LinkedList; +import java.util.StringTokenizer; + +public class Select extends AbstractCommand +{ + + public Select(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Perform a selection."; + } + + public String usage() + { + return "select virtualhost |exchange |queue | msg id="; + } + + public String getCommand() + { + return "select"; + } + + public void execute(String... args) + { + assert args.length > 2; + assert args[0].equals("select"); + + if (args.length < 3) + { + if (args[1].equals("show")) + { + doSelect(args[1], null); + } + else + { + _console.print("select : unknown command:"); + _console.println(help()); + } + } + else + { + if (args[1].equals("virtualhost") + || args[1].equals("vhost") + || args[1].equals("exchange") + || args[1].equals("queue") + || args[1].equals("msg") + ) + { + doSelect(args[1], args[2]); + } + else + { + _console.println(help()); + } + } + } + + private void doSelect(String type, String item) + { + if (type.equals("virtualhost")) + { + + VirtualHost vhost = ApplicationRegistry.getInstance() + .getVirtualHostRegistry().getVirtualHost(item); + + if (vhost == null) + { + _console.println("Virtualhost '" + item + "' not found."); + } + else + { + _tool.getState().setVhost(vhost); + } + } + + if (type.equals("exchange")) + { + + VirtualHost vhost = _tool.getState().getVhost(); + + if (vhost == null) + { + _console.println("No Virtualhost open. Open a Virtualhost first."); + return; + } + + + Exchange exchange = vhost.getExchangeRegistry().getExchange(new AMQShortString(item)); + + if (exchange == null) + { + _console.println("Exchange '" + item + "' not found."); + } + else + { + _tool.getState().setExchange(exchange); + } + + if (_tool.getState().getQueue() != null) + { + if (!exchange.isBound(_tool.getState().getQueue())) + { + _tool.getState().setQueue(null); + } + } + } + + if (type.equals("queue")) + { + VirtualHost vhost = _tool.getState().getVhost(); + + if (vhost == null) + { + _console.println("No Virtualhost open. Open a Virtualhost first."); + return; + } + + AMQQueue queue = vhost.getQueueRegistry().getQueue(new AMQShortString(item)); + + if (queue == null) + { + _console.println("Queue '" + item + "' not found."); + } + else + { + _tool.getState().setQueue(queue); + + if (_tool.getState().getExchange() == null) + { + for (AMQShortString exchangeName : vhost.getExchangeRegistry().getExchangeNames()) + { + Exchange exchange = vhost.getExchangeRegistry().getExchange(exchangeName); + if (exchange.isBound(queue)) + { + _tool.getState().setExchange(exchange); + break; + } + } + } + + //remove the message selection + _tool.getState().setMessages((java.util.List) null); + } + } + + if (type.equals("msg")) + { + if (item.startsWith("id=")) + { + StringTokenizer tok = new StringTokenizer(item.substring(item.indexOf("=") + 1), ","); + + java.util.List msgids = null; + + if (tok.hasMoreTokens()) + { + msgids = new LinkedList(); + } + + while (tok.hasMoreTokens()) + { + String next = tok.nextToken(); + if (next.contains("-")) + { + Long start = Long.parseLong(next.substring(0, next.indexOf("-"))); + Long end = Long.parseLong(next.substring(next.indexOf("-") + 1)); + + if (end >= start) + { + for (long l = start; l <= end; l++) + { + msgids.add(l); + } + } + } + else + { + msgids.add(Long.parseLong(next)); + } + } + + _tool.getState().setMessages(msgids); + } + + } + + if (type.equals("show")) + { + _console.println(_tool.getState().toString()); + if (_tool.getState().getMessages() != null) + { + _console.print("Msgs:"); + for (Long l : _tool.getState().getMessages()) + { + _console.print(" " + l); + } + _console.println(""); + } + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java new file mode 100644 index 0000000000..2fa017fc64 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java @@ -0,0 +1,515 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.messagestore.commands; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryImpl; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +import java.util.LinkedList; +import java.util.List; + +public class Show extends AbstractCommand +{ + protected boolean _amqHeaders = false; + protected boolean _routing = false; + protected boolean _msgHeaders = false; + + public Show(MessageStoreTool tool) + { + super(tool); + } + + public String help() + { + return "Shows the messages headers."; + } + + public String usage() + { + return getCommand() + " [show=[all],[msgheaders],[amqheaders],[routing]] [id=]"; + } + + public String getCommand() + { + return "show"; + } + + public void execute(String... args) + { + assert args.length > 0; + assert args[0].equals(getCommand()); + + if (args.length < 2) + { + parseArgs("all"); + } + else + { + parseArgs(args); + } + + performShow(); + } + + protected void parseArgs(String... args) + { + List msgids = null; + + if (args.length >= 2) + { + for (String arg : args) + { + if (arg.startsWith("show=")) + { + _msgHeaders = arg.contains("msgheaders") || arg.contains("all"); + _amqHeaders = arg.contains("amqheaders") || arg.contains("all"); + _routing = arg.contains("routing") || arg.contains("all"); + } + + if (arg.startsWith("id=")) + { + _tool.getState().setMessages(msgids); + } + }//for args + }// if args > 2 + } + + protected void performShow() + { + if (_tool.getState().getVhost() == null) + { + _console.println("No Virtualhost selected. 'DuSelect' a Virtualhost first."); + return; + } + + AMQQueue _queue = _tool.getState().getQueue(); + + List msgids = _tool.getState().getMessages(); + + if (_queue != null) + { + List messages = _queue.getMessagesOnTheQueue(); + if (messages == null || messages.size() == 0) + { + _console.println("No messages on queue"); + return; + } + + List data = createMessageData(msgids, messages, _amqHeaders, _routing, _msgHeaders); + if (data != null) + { + _console.printMap(null, data); + } + else + { + String message = "No data to display."; + if (msgids != null) + { + message += " Is message selection correct? " + _tool.getState().printMessages(); + } + _console.println(message); + } + + } + else + { + _console.println("No Queue specified to show."); + } + } + + /** + * Create the list data for display from the messages. + * + * @param msgids The list of message ids to display + * @param messages A list of messages to format and display. + * @param showHeaders should the header info be shown + * @param showRouting show the routing info be shown + * @param showMessageHeaders show the msg headers be shown + * @return the formated data lists for printing + */ + protected List createMessageData(List msgids, List messages, boolean showHeaders, boolean showRouting, + boolean showMessageHeaders) + { + + // Currenly exposed message properties +// //Printing the content Body +// msg.getContentBodyIterator(); +// //Print the Headers +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppId(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getAppIdAsString(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getClusterId(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getContentType(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getCorrelationId(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getDeliveryMode(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getEncoding(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getExpiration(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getMessageId(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPriority(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getPropertyFlags(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getReplyTo(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getTimestamp(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getType(); +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getUserId(); +// +// //Print out all the property names +// ((BasicContentHeaderProperties)msg.getContentHeaderBody().properties).getHeaders().getPropertyNames(); +// +// msg.getMessageId(); +// msg.getSize(); +// msg.getArrivalTime(); + +// msg.getDeliveredSubscription(); +// msg.getDeliveredToConsumer(); +// msg.getMessageHandle(); +// msg.getMessageId(); +// msg.getMessagePublishInfo(); +// msg.getPublisher(); + +// msg.getStoreContext(); +// msg.isAllContentReceived(); +// msg.isPersistent(); +// msg.isRedelivered(); +// msg.isRejectedBy(); +// msg.isTaken(); + + //Header setup + + List data = new LinkedList(); + + List id = new LinkedList(); + data.add(id); + id.add(Columns.ID.name()); + id.add(Console.ROW_DIVIDER); + + List exchange = new LinkedList(); + List routingkey = new LinkedList(); + List immediate = new LinkedList(); + List mandatory = new LinkedList(); + if (showRouting) + { + data.add(exchange); + exchange.add(Columns.Exchange.name()); + exchange.add(Console.ROW_DIVIDER); + + data.add(routingkey); + routingkey.add(Columns.RoutingKey.name()); + routingkey.add(Console.ROW_DIVIDER); + + data.add(immediate); + immediate.add(Columns.isImmediate.name()); + immediate.add(Console.ROW_DIVIDER); + + data.add(mandatory); + mandatory.add(Columns.isMandatory.name()); + mandatory.add(Console.ROW_DIVIDER); + } + + List size = new LinkedList(); + List appid = new LinkedList(); + List clusterid = new LinkedList(); + List contenttype = new LinkedList(); + List correlationid = new LinkedList(); + List deliverymode = new LinkedList(); + List encoding = new LinkedList(); + List arrival = new LinkedList(); + List expiration = new LinkedList(); + List priority = new LinkedList(); + List propertyflag = new LinkedList(); + List replyto = new LinkedList(); + List timestamp = new LinkedList(); + List type = new LinkedList(); + List userid = new LinkedList(); + List ispersitent = new LinkedList(); + List isredelivered = new LinkedList(); + List isdelivered = new LinkedList(); + + data.add(size); + size.add(Columns.Size.name()); + size.add(Console.ROW_DIVIDER); + + if (showHeaders) + { + data.add(ispersitent); + ispersitent.add(Columns.isPersistent.name()); + ispersitent.add(Console.ROW_DIVIDER); + + data.add(isredelivered); + isredelivered.add(Columns.isRedelivered.name()); + isredelivered.add(Console.ROW_DIVIDER); + + data.add(isdelivered); + isdelivered.add(Columns.isDelivered.name()); + isdelivered.add(Console.ROW_DIVIDER); + + data.add(appid); + appid.add(Columns.App_ID.name()); + appid.add(Console.ROW_DIVIDER); + + data.add(clusterid); + clusterid.add(Columns.Cluster_ID.name()); + clusterid.add(Console.ROW_DIVIDER); + + data.add(contenttype); + contenttype.add(Columns.Content_Type.name()); + contenttype.add(Console.ROW_DIVIDER); + + data.add(correlationid); + correlationid.add(Columns.Correlation_ID.name()); + correlationid.add(Console.ROW_DIVIDER); + + data.add(deliverymode); + deliverymode.add(Columns.Delivery_Mode.name()); + deliverymode.add(Console.ROW_DIVIDER); + + data.add(encoding); + encoding.add(Columns.Encoding.name()); + encoding.add(Console.ROW_DIVIDER); + + data.add(arrival); + expiration.add(Columns.Arrival.name()); + expiration.add(Console.ROW_DIVIDER); + + data.add(expiration); + expiration.add(Columns.Expiration.name()); + expiration.add(Console.ROW_DIVIDER); + + data.add(priority); + priority.add(Columns.Priority.name()); + priority.add(Console.ROW_DIVIDER); + + data.add(propertyflag); + propertyflag.add(Columns.Property_Flag.name()); + propertyflag.add(Console.ROW_DIVIDER); + + data.add(replyto); + replyto.add(Columns.ReplyTo.name()); + replyto.add(Console.ROW_DIVIDER); + + data.add(timestamp); + timestamp.add(Columns.Timestamp.name()); + timestamp.add(Console.ROW_DIVIDER); + + data.add(type); + type.add(Columns.Type.name()); + type.add(Console.ROW_DIVIDER); + + data.add(userid); + userid.add(Columns.UserID.name()); + userid.add(Console.ROW_DIVIDER); + } + + List msgHeaders = new LinkedList(); + if (showMessageHeaders) + { + data.add(msgHeaders); + msgHeaders.add(Columns.MsgHeaders.name()); + msgHeaders.add(Console.ROW_DIVIDER); + } + + //Add create the table of data + for (QueueEntry entry : messages) + { + AMQMessage msg = entry.getMessage(); + if (!includeMsg(msg, msgids)) + { + continue; + } + + id.add(msg.getMessageId().toString()); + + size.add("" + msg.getSize()); + + arrival.add("" + msg.getArrivalTime()); + + try + { + ispersitent.add(msg.isPersistent() ? "true" : "false"); + } + catch (AMQException e) + { + ispersitent.add("n/a"); + } + + isredelivered.add(msg.isRedelivered() ? "true" : "false"); + + isdelivered.add(msg.getDeliveredToConsumer() ? "true" : "false"); + +// msg.getMessageHandle(); + + BasicContentHeaderProperties headers = null; + + try + { + headers = ((BasicContentHeaderProperties) msg.getContentHeaderBody().properties); + } + catch (AMQException e) + { + //ignore +// commandError("Unable to read properties for message: " + e.getMessage(), null); + } + + if (headers != null) + { + String appidS = headers.getAppIdAsString(); + appid.add(appidS == null ? "null" : appidS); + + String clusterS = headers.getClusterIdAsString(); + clusterid.add(clusterS == null ? "null" : clusterS); + + String contentS = headers.getContentTypeAsString(); + contenttype.add(contentS == null ? "null" : contentS); + + String correlationS = headers.getCorrelationIdAsString(); + correlationid.add(correlationS == null ? "null" : correlationS); + + deliverymode.add("" + headers.getDeliveryMode()); + + AMQShortString encodeSS = headers.getEncoding(); + encoding.add(encodeSS == null ? "null" : encodeSS.toString()); + + expiration.add("" + headers.getExpiration()); + + FieldTable headerFT = headers.getHeaders(); + msgHeaders.add(headerFT == null ? "none" : "" + headerFT.toString()); + + priority.add("" + headers.getPriority()); + propertyflag.add("" + headers.getPropertyFlags()); + + AMQShortString replytoSS = headers.getReplyTo(); + replyto.add(replytoSS == null ? "null" : replytoSS.toString()); + + timestamp.add("" + headers.getTimestamp()); + + AMQShortString typeSS = headers.getType(); + type.add(typeSS == null ? "null" : typeSS.toString()); + + AMQShortString useridSS = headers.getUserId(); + userid.add(useridSS == null ? "null" : useridSS.toString()); + + MessagePublishInfo info = null; + try + { + info = msg.getMessagePublishInfo(); + } + catch (AMQException e) + { + //ignore + } + + if (info != null) + { + AMQShortString exchangeSS = info.getExchange(); + exchange.add(exchangeSS == null ? "null" : exchangeSS.toString()); + + AMQShortString routingkeySS = info.getRoutingKey(); + routingkey.add(routingkeySS == null ? "null" : routingkeySS.toString()); + + immediate.add(info.isImmediate() ? "true" : "false"); + mandatory.add(info.isMandatory() ? "true" : "false"); + } + +// msg.getPublisher(); -- only used in clustering +// msg.getStoreContext(); +// msg.isAllContentReceived(); + + }// if headers!=null + +// need to access internal map and do lookups. +// msg.isTaken(); +// msg.getDeliveredSubscription(); +// msg.isRejectedBy(); + + } + + // if id only had the header and the divider in it then we have no data to display + if (id.size() == 2) + { + return null; + } + return data; + } + + protected boolean includeMsg(AMQMessage msg, List msgids) + { + if (msgids == null) + { + return true; + } + + Long msgid = msg.getMessageId(); + + boolean found = false; + + if (msgids != null) + { + //check msgid is in msgids + for (Long l : msgids) + { + if (l.equals(msgid)) + { + found = true; + break; + } + } + } + return found; + } + + public enum Columns + { + ID, + Size, + Exchange, + RoutingKey, + isImmediate, + isMandatory, + isPersistent, + isRedelivered, + isDelivered, + App_ID, + Cluster_ID, + Content_Type, + Correlation_ID, + Delivery_Mode, + Encoding, + Arrival, + Expiration, + Priority, + Property_Flag, + ReplyTo, + Timestamp, + Type, + UserID, + MsgHeaders + } +} + + diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java new file mode 100644 index 0000000000..c27c52eb8e --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.security; + +import org.apache.commons.codec.binary.Base64; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.DigestException; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; + +public class Passwd +{ + public static void main(String args[]) throws NoSuchAlgorithmException, DigestException, IOException + { + if (args.length != 2) + { + System.out.println("Passwd "); + System.exit(0); + } + + byte[] data = args[1].getBytes("utf-8"); + + MessageDigest md = MessageDigest.getInstance("MD5"); + + for (byte b : data) + { + md.update(b); + } + + byte[] digest = md.digest(); + + Base64 b64 = new Base64(); + + byte[] encoded = b64.encode(digest); + + output(args[0], encoded); + } + + private static void output(String user, byte[] encoded) throws IOException + { + +// File passwdFile = new File("qpid.passwd"); + + PrintStream ps = new PrintStream(System.out); + + user += ":"; + ps.write(user.getBytes("utf-8")); + + for (byte b : encoded) + { + ps.write(b); + } + + ps.println(); + + ps.flush(); + ps.close(); + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java new file mode 100644 index 0000000000..986fea32cc --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.utils; + +public interface CommandParser +{ + /** + * If there is more than one command received on the last parse request. + * + * Subsequent calls to parse will utilise this input rather than reading new data from the input source + * @return boolean + */ + boolean more(); + + /** + * True if the currently parsed command has been requested as a background operation + * + * @return boolean + */ + boolean isBackground(); + + /** + * Parses user commands, and groups tokens in the + * String[] format that all Java main's love. + * + * If more than one command is provided in one input line then the more() method will return true. + * A subsequent call to parse() will continue to parse that input line before reading new input. + * + * @return input split in args[] format; null if eof. + * @throws java.io.IOException if there is a problem reading from the input stream + */ + String[] parse() throws java.io.IOException; +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java new file mode 100644 index 0000000000..cf457d1ea5 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.utils; + +import java.util.List; + +public interface Console +{ + public enum CellFormat + { + CENTRED, LEFT, RIGHT + } + + public static String ROW_DIVIDER = "*divider"; + + public void print(String... message); + + public void println(String... message); + + public String readln(); + + /** + * Reads and parses the command line. + * + * + * @return The next command or null + */ + public String[] readCommand(); + + public CommandParser getCommandParser(); + + public void setCommandParser(CommandParser parser); + + /** + * + * Prints the list of String nicely. + * + * +-------------+ + * | Heading | + * +-------------+ + * | Item 1 | + * | Item 2 | + * | Item 3 | + * +-------------+ + * + * @param hasTitle should list[0] be used as a heading + * @param list The list of Strings to display + */ + public void displayList(boolean hasTitle, String... list); + + /** + * + * Prints the list of String nicely. + * + * +----------------------------+ + * | Heading | + * +----------------------------+ + * | title | title | .. + * +----------------------------+ + * | Item 2 | value 2 | .. + * +----------------------------+ (*divider) + * | Item 3 | value 2 | .. + * +----------------------------+ + * + * @param title The title to display if any + * @param entries the entries to display in a map. + */ + void printMap(String title, List entries); + + + public void close(); +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java new file mode 100644 index 0000000000..09444ccdd7 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.StringTokenizer; + +public class SimpleCommandParser implements CommandParser +{ + private static final String COMMAND_SEPERATOR = ";"; + + /** Input source of commands */ + protected BufferedReader _reader; + + /** The next list of commands from the command line */ + private StringBuilder _nextCommand = null; + + public SimpleCommandParser(BufferedReader reader) + { + _reader = reader; + } + + public boolean more() + { + return _nextCommand != null; + } + + public boolean isBackground() + { + return false; + } + + public String[] parse() throws IOException + { + String[] commands = null; + + String input = null; + + if (_nextCommand == null) + { + input = _reader.readLine(); + } + else + { + input = _nextCommand.toString(); + _nextCommand = null; + } + + if (input == null) + { + return null; + } + + StringTokenizer tok = new StringTokenizer(input, " "); + + int tokenCount = tok.countTokens(); + int index = 0; + + if (tokenCount > 0) + { + commands = new String[tokenCount]; + boolean commandComplete = false; + + while (tok.hasMoreTokens()) + { + String next = tok.nextToken(); + + if (next.equals(COMMAND_SEPERATOR)) + { + commandComplete = true; + _nextCommand = new StringBuilder(); + continue; + } + + if (commandComplete) + { + _nextCommand.append(next); + _nextCommand.append(" "); + } + else + { + commands[index] = next; + index++; + } + } + + } + + //Reduce the String[] if not all the tokens were used in this command. + // i.e. there is more than one command on the line. + if (index != tokenCount) + { + String[] shortCommands = new String[index]; + System.arraycopy(commands, 0, shortCommands, 0, index); + return shortCommands; + } + else + { + return commands; + } + } +} diff --git a/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java new file mode 100644 index 0000000000..ec080a4611 --- /dev/null +++ b/RC6/qpid/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java @@ -0,0 +1,363 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.tools.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +public class SimpleConsole implements Console +{ + /** SLF4J Logger. */ + private static Logger _devlog = LoggerFactory.getLogger(SimpleConsole.class); + + /** Console Writer. */ + protected static BufferedWriter _consoleWriter; + + /** Console Reader. */ + protected static BufferedReader _consoleReader; + + /** Parser for command-line input. */ + protected CommandParser _parser; + + public SimpleConsole(BufferedWriter writer, BufferedReader reader) + { + _consoleWriter = writer; + _consoleReader = reader; + _parser = new SimpleCommandParser(_consoleReader); + } + + public void print(String... message) + { + try + { + for (String s : message) + { + _consoleWriter.write(s); + } + _consoleWriter.flush(); + } + catch (IOException e) + { + _devlog.error(e.getMessage() + ": Occured whilst trying to write:" + message); + } + + } + + public void println(String... message) + { + print(message); + print(System.getProperty("line.separator")); + } + + + public String readln() + { + try + { + return _consoleReader.readLine(); + } + catch (IOException e) + { + _devlog.debug("Unable to read input due to:" + e.getMessage()); + return null; + } + } + + public String[] readCommand() + { + try + { + return _parser.parse(); + } + catch (IOException e) + { + _devlog.error("Error reading command:" + e.getMessage()); + return new String[0]; + } + } + + public CommandParser getCommandParser() + { + return _parser; + } + + public void setCommandParser(CommandParser parser) + { + _parser = parser; + } + + public void displayList(boolean hasTitle, String... list) + { + java.util.List data = new LinkedList(); + + java.util.List values = new LinkedList(); + + data.add(values); + + for (String value : list) + { + values.add(value); + } + + if (hasTitle) + { + values.add(1, "*divider"); + } + + printMap(null, data); + } + + /** + * + * Prints the list of String nicely. + * + * +----------------------------+ + * | Heading | + * +----------------------------+ + * | title | title | .. + * +----------------------------+ + * | Item 2 | value 2 | .. + * | Item 3 | value 2 | .. + * +----------------------------+ + * + * @param title The title to display if any + * @param entries the entries to display in a map. + */ + public void printMap(String title, java.util.List entries) + { + try + { + int columns = entries.size(); + + int[] columnWidth = new int[columns]; + + // calculate row count + int rowMax = 0; + + //the longest item + int itemMax = 0; + + for (int i = 0; i < columns; i++) + { + int columnIRowMax = entries.get(i).size(); + + if (columnIRowMax > rowMax) + { + rowMax = columnIRowMax; + } + for (Object values : entries.get(i)) + { + if (values.toString().equals(Console.ROW_DIVIDER)) + { + continue; + } + + int itemLength = values.toString().length(); + + //note for single width + if (itemLength > itemMax) + { + itemMax = itemLength; + } + + //note for mulit width + if (itemLength > columnWidth[i]) + { + columnWidth[i] = itemLength; + } + + } + } + + int tableWidth = 0; + + + for (int i = 0; i < columns; i++) + { + // plus 2 for the space padding + columnWidth[i] += 2; + } + for (int size : columnWidth) + { + tableWidth += size; + } + tableWidth += (columns - 1); + + if (title != null) + { + if (title.length() > tableWidth) + { + tableWidth = title.length(); + } + + printCellRow("+", "-", tableWidth); + + printCell(CellFormat.CENTRED, "|", tableWidth, " " + title + " ", 0); + _consoleWriter.newLine(); + + } + + //put top line | or bottom of title + printCellRow("+", "-", tableWidth); + + //print the table data + int row = 0; + + for (; row < rowMax; row++) + { + for (int i = 0; i < columns; i++) + { + java.util.List columnData = entries.get(i); + + String value; + // does this column have a value for this row + if (columnData.size() > row) + { + value = " " + columnData.get(row).toString() + " "; + } + else + { + value = " "; + } + + if (i == 0 && value.equals(" " + Console.ROW_DIVIDER + " ")) + { + printCellRow("+", "-", tableWidth); + //move on to the next row + break; + } + else + { + printCell(CellFormat.LEFT, "|", columnWidth[i], value, i); + } + + // if it is the last row then do a new line. + if (i == columns - 1) + { + _consoleWriter.newLine(); + } + } + } + + printCellRow("+", "-", tableWidth); + + } + catch (IOException e) + { + _devlog.error(e.getMessage() + ": Occured whilst trying to write."); + } + } + + public void close() + { + + try + { + _consoleReader.close(); + } + catch (IOException e) + { + _devlog.error(e.getMessage() + ": Occured whilst trying to close reader."); + } + + try + { + + _consoleWriter.close(); + } + catch (IOException e) + { + _devlog.error(e.getMessage() + ": Occured whilst trying to close writer."); + } + + } + + private void printCell(CellFormat format, String edge, int cellWidth, String cell, int column) throws IOException + { + int pad = cellWidth - cell.length(); + + if (column == 0) + { + _consoleWriter.write(edge); + } + + switch (format) + { + case CENTRED: + printPad(" ", pad / 2); + break; + case RIGHT: + printPad(" ", pad); + break; + } + + _consoleWriter.write(cell); + + + switch (format) + { + case CENTRED: + // if pad isn't even put the extra one on the right + if (pad % 2 == 0) + { + printPad(" ", pad / 2); + } + else + { + printPad(" ", (pad / 2) + 1); + } + break; + case LEFT: + printPad(" ", pad); + break; + } + + + _consoleWriter.write(edge); + + } + + private void printCellRow(String edge, String mid, int cellWidth) throws IOException + { + _consoleWriter.write(edge); + + printPad(mid, cellWidth); + + _consoleWriter.write(edge); + _consoleWriter.newLine(); + } + + private void printPad(String padChar, int count) throws IOException + { + for (int i = 0; i < count; i++) + { + _consoleWriter.write(padChar); + } + } + + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java new file mode 100644 index 0000000000..5fbf9484f7 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ExtractResendAndRequeueTest.java @@ -0,0 +1,255 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; +import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; +import org.apache.qpid.server.queue.MockQueueEntry; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.SimpleQueueEntryList; +import org.apache.qpid.server.queue.MockAMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MockAMQQueue; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueEntryIterator; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.MockSubscription; +import org.apache.qpid.AMQException; + +import java.util.Map; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Iterator; + +/** + * QPID-1385 : Race condition between added to unacked map and resending due to a rollback. + * + * In AMQChannel _unackedMap.clear() was done after the visit. This meant that the clear was not in the same + * synchronized block as as the preparation to resend. + * + * This clearing/prep for resend was done as a result of the rollback call. HOWEVER, the delivery thread was still + * in the process of sending messages to the client. It is therefore possible that a message could block on the + * _unackedMap lock waiting for the visit to compelete so that it can add the new message to the unackedMap.... + * which is then cleared by the resend/rollback thread. + * + * This problem was encountered by the testSend2ThenRollback test. + * + * To try and increase the chance of the race condition occuring this test will send multiple messages so that the + * delivery thread will be in progress while the rollback method is called. Hopefully this will cause the + * deliveryTag to be lost + */ +public class ExtractResendAndRequeueTest extends TestCase +{ + + UnacknowledgedMessageMapImpl _unacknowledgedMessageMap; + private static final int INITIAL_MSG_COUNT = 10; + private AMQQueue _queue = new MockAMQQueue(); + private LinkedList _referenceList = new LinkedList(); + + @Override + public void setUp() throws AMQException + { + _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(100); + + long id = 0; + SimpleQueueEntryList list = new SimpleQueueEntryList(_queue); + + // Add initial messages to QueueEntryList + for (int count = 0; count < INITIAL_MSG_COUNT; count++) + { + AMQMessage msg = new MockAMQMessage(id); + + list.add(msg); + + //Increment ID; + id++; + } + + // Iterate through the QueueEntryList and add entries to unacknowledgeMessageMap and referecenList + QueueEntryIterator queueEntries = list.iterator(); + while(queueEntries.advance()) + { + QueueEntry entry = queueEntries.getNode(); + _unacknowledgedMessageMap.add(entry.getMessage().getMessageId(), entry); + + // Store the entry for future inspection + _referenceList.add(entry); + } + + assertEquals("Map does not contain correct setup data", INITIAL_MSG_COUNT, _unacknowledgedMessageMap.size()); + } + + /** + * Helper method to create a new subscription and aquire the given messages. + * + * @param messageList The messages to aquire + * + * @return Subscription that performed the aquire + */ + private Subscription createSubscriptionAndAquireMessages(LinkedList messageList) + { + Subscription subscription = new MockSubscription(); + + // Aquire messages in subscription + for (QueueEntry entry : messageList) + { + entry.acquire(subscription); + } + + return subscription; + } + + /** + * This is the normal consumer rollback method. + * + * An active consumer that has aquired messages expects those messasges to be reset when rollback is requested. + * + * This test validates that the msgToResend map includes all the messages and none are left behind. + * + * @throws AMQException the visit interface throws this + */ + public void testResend() throws AMQException + { + //We don't need the subscription object here. + createSubscriptionAndAquireMessages(_referenceList); + + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + // requeueIfUnabletoResend doesn't matter here. + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, true, new StoreContext())); + + assertEquals("Message count for resend not correct.", INITIAL_MSG_COUNT, msgToResend.size()); + assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); + assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); + } + + /** + * This is the normal consumer close method. + * + * When a consumer that has aquired messages expects closes the messages that it has aquired should be removed from + * the unacknowledgeMap and placed in msgToRequeue + * + * This test validates that the msgToRequeue map includes all the messages and none are left behind. + * + * @throws AMQException the visit interface throws this + */ + public void testRequeueDueToSubscriptionClosure() throws AMQException + { + Subscription subscription = createSubscriptionAndAquireMessages(_referenceList); + + // Close subscription + subscription.close(); + + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + // requeueIfUnabletoResend doesn't matter here. + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, true, new StoreContext())); + + assertEquals("Message count for resend not correct.", 0, msgToResend.size()); + assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size()); + assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); + } + + /** + * If the subscription is null, due to message being retrieved via a GET, And we request that messages are requeued + * requeueIfUnabletoResend(set to true) then all messages should be sent to the msgToRequeue map. + * + * @throws AMQException the visit interface throws this + */ + + public void testRequeueDueToMessageHavingNoConsumerTag() throws AMQException + { + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + // requeueIfUnabletoResend = true so all messages should go to msgToRequeue + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, true, new StoreContext())); + + assertEquals("Message count for resend not correct.", 0, msgToResend.size()); + assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size()); + assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); + } + + /** + * If the subscription is null, due to message being retrieved via a GET, And we request that we don't + * requeueIfUnabletoResend(set to false) then all messages should be dropped as we do not have a dead letter queue. + * + * @throws AMQException the visit interface throws this + */ + + public void testDrop() throws AMQException + { + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + // requeueIfUnabletoResend = false so all messages should be dropped all maps should be empty + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, false, new StoreContext())); + + assertEquals("Message count for resend not correct.", 0, msgToResend.size()); + assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); + assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); + + + for (QueueEntry entry : _referenceList) + { + assertTrue("Message was not discarded", entry.isDeleted()); + } + + } + + /** + * If the subscription is null, due to message being retrieved via a GET, AND the queue upon which the message was + * delivered has been deleted then it is not possible to requeue. Currently we simply discar the message but in the + * future we may wish to dead letter the message. + * + * Validate that at the end of the visit all Maps are empty and all messages are marked as deleted + * + * @throws AMQException the visit interface throws this + */ + public void testDiscard() throws AMQException + { + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); + + _queue.delete(); + + // requeueIfUnabletoResend : value doesn't matter here as queue has been deleted + _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, + msgToResend, false, new StoreContext())); + + assertEquals("Message count for resend not correct.", 0, msgToResend.size()); + assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); + assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); + + for (QueueEntry entry : _referenceList) + { + assertTrue("Message was not discarded", entry.isDeleted()); + } + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java new file mode 100644 index 0000000000..59543874b4 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.log4j.Logger; +import org.apache.log4j.Level; + +import java.io.InputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; + +public class RunBrokerWithCommand +{ + public static void main(String[] args) + { + //Start the broker + try + { + String[] fudge = args.clone(); + + // Override the first value which is the command we are going to run later. + fudge[0] = "-v"; + new Main(fudge).startup(); + } + catch (Exception e) + { + System.err.println("Unable to start broker due to: " + e.getMessage()); + + e.printStackTrace(); + exit(1); + } + + Logger.getRootLogger().setLevel(Level.ERROR); + + //run command + try + { + Process task = Runtime.getRuntime().exec(args[0]); + System.err.println("Started Proccess: " + args[0]); + + InputStream inputStream = task.getInputStream(); + + InputStream errorStream = task.getErrorStream(); + + Thread out = new Thread(new Outputter("[OUT]", new BufferedReader(new InputStreamReader(inputStream)))); + Thread err = new Thread(new Outputter("[ERR]", new BufferedReader(new InputStreamReader(errorStream)))); + + out.start(); + err.start(); + + out.join(); + err.join(); + + System.err.println("Waiting for process to exit: " + args[0]); + task.waitFor(); + System.err.println("Done Proccess: " + args[0]); + + } + catch (IOException e) + { + System.err.println("Proccess had problems: " + e.getMessage()); + e.printStackTrace(System.err); + exit(1); + } + catch (InterruptedException e) + { + System.err.println("Proccess had problems: " + e.getMessage()); + e.printStackTrace(System.err); + + exit(1); + } + + + exit(0); + } + + private static void exit(int i) + { + Logger.getRootLogger().setLevel(Level.INFO); + System.exit(i); + } + + static class Outputter implements Runnable + { + + BufferedReader reader; + String prefix; + + Outputter(String s, BufferedReader r) + { + prefix = s; + reader = r; + } + + public void run() + { + String line; + try + { + while ((line = reader.readLine()) != null) + { + System.out.println(prefix + line); + } + } + catch (IOException e) + { + System.out.println("Error occured reading; " + e.getMessage()); + } + } + + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java new file mode 100644 index 0000000000..a0304a7b01 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/SelectorParserTest.java @@ -0,0 +1,128 @@ +package org.apache.qpid.server; + +import junit.framework.TestCase; +import org.apache.qpid.server.filter.JMSSelectorFilter; +import org.apache.qpid.AMQException;/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 SelectorParserTest extends TestCase +{ + public void testSelectorWithHyphen() + { + testPass("Cost = 2 AND \"property-with-hyphen\" = 'wibble'"); + } + + public void testLike() + { + testFail("Cost LIKE 2"); + testPass("Cost LIKE 'Hello'"); + } + + public void testStringQuoted() + { + testPass("string = 'Test'"); + } + + public void testProperty() + { + testPass("prop1 = prop2"); + } + + public void testPropertyNames() + { + testPass("$min= TRUE AND _max= FALSE AND Prop_2 = true AND prop$3 = false"); + } + + public void testProtected() + { + testFail("NULL = 0 "); + testFail("TRUE = 0 "); + testFail("FALSE = 0 "); + testFail("NOT = 0 "); + testFail("AND = 0 "); + testFail("OR = 0 "); + testFail("BETWEEN = 0 "); + testFail("LIKE = 0 "); + testFail("IN = 0 "); + testFail("IS = 0 "); + testFail("ESCAPE = 0 "); + } + + + public void testBoolean() + { + testPass("min= TRUE AND max= FALSE "); + testPass("min= true AND max= false"); + } + + public void testDouble() + { + testPass("positive=31E2 AND negative=-31.4E3"); + testPass("min=" + Double.MIN_VALUE + " AND max=" + Double.MAX_VALUE); + } + + public void testLong() + { + testPass("minLong=" + Long.MIN_VALUE + "L AND maxLong=" + Long.MAX_VALUE + "L"); + } + + public void testInt() + { + testPass("minInt=" + Integer.MIN_VALUE + " AND maxInt=" + Integer.MAX_VALUE); + } + + public void testSigned() + { + testPass("negative=-42 AND positive=+42"); + } + + public void testOctal() + { + testPass("octal=042"); + } + + + private void testPass(String selector) + { + try + { + new JMSSelectorFilter(selector); + } + catch (AMQException e) + { + fail("Selector '" + selector + "' was not parsed :" + e.getMessage()); + } + } + + private void testFail(String selector) + { + try + { + new JMSSelectorFilter(selector); + fail("Selector '" + selector + "' was parsed "); + } + catch (AMQException e) + { + //normal path + } + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java new file mode 100644 index 0000000000..9ef4af2932 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/ack/AcknowledgeTest.java @@ -0,0 +1,120 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.ack; + + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.util.InternalBrokerBaseCase; + +import java.util.List; + +public class AcknowledgeTest extends InternalBrokerBaseCase +{ + + public void testTransactionalSingleAck() throws AMQException + { + _channel.setLocalTransactional(); + runMessageAck(1, 1, 1, false, 0); + } + + public void testTransactionalMultiAck() throws AMQException + { + _channel.setLocalTransactional(); + runMessageAck(10, 1, 5, true, 5); + } + + public void testTransactionalAckAll() throws AMQException + { + _channel.setLocalTransactional(); + runMessageAck(10, 1, 0, true, 0); + } + + public void testNonTransactionalSingleAck() throws AMQException + { + runMessageAck(1, 1, 1, false, 0); + } + + public void testNonTransactionalMultiAck() throws AMQException + { + runMessageAck(10, 1, 5, true, 5); + } + + public void testNonTransactionalAckAll() throws AMQException + { + runMessageAck(10, 1, 0, true, 0); + } + + protected void runMessageAck(int sendMessageCount, long firstDeliveryTag, long acknowledgeDeliveryTag, boolean acknowldegeMultiple, int remainingUnackedMessages) throws AMQException + { + //Check store is empty + checkStoreContents(0); + + //Send required messsages to the queue + publishMessages(_session, _channel, sendMessageCount); + + if (_channel.isTransactional()) + { + _channel.commit(); + } + + //Ensure they are stored + checkStoreContents(sendMessageCount); + + //Check that there are no unacked messages + assertEquals("Channel should have no unacked msgs ", 0, _channel.getUnacknowledgedMessageMap().size()); + + //Subscribe to the queue + AMQShortString subscriber = subscribe(_session, _channel, _queue); + + _queue.deliverAsync(); + + //Wait for the messages to be delivered + _session.awaitDelivery(sendMessageCount); + + //Check that they are all waiting to be acknoledged + assertEquals("Channel should have unacked msgs", sendMessageCount, _channel.getUnacknowledgedMessageMap().size()); + + List messages = _session.getDelivers(_channel.getChannelId(), subscriber, sendMessageCount); + + //Double check we received the right number of messages + assertEquals(sendMessageCount, messages.size()); + + //Check that the first message has the expected deliveryTag + assertEquals("First message does not have expected deliveryTag", firstDeliveryTag, messages.get(0).getDeliveryTag()); + + //Send required Acknowledgement + _channel.acknowledgeMessage(acknowledgeDeliveryTag, acknowldegeMultiple); + + if (_channel.isTransactional()) + { + _channel.commit(); + } + + // Check Remaining Acknowledgements + assertEquals("Channel unacked msgs count incorrect", remainingUnackedMessages, _channel.getUnacknowledgedMessageMap().size()); + + //Check store contents are also correct. + checkStoreContents(remainingUnackedMessages); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java new file mode 100644 index 0000000000..3b83190e42 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.configuration; + +import org.apache.qpid.configuration.PropertyException; +import org.apache.qpid.configuration.PropertyUtils; + +import junit.framework.TestCase; + +// TODO: This belongs in the "common" module. +public class TestPropertyUtils extends TestCase +{ + public void testSimpleExpansion() throws PropertyException + { + System.setProperty("banana", "fruity"); + String expandedProperty = PropertyUtils.replaceProperties("${banana}"); + assertEquals(expandedProperty, "fruity"); + } + + public void testDualExpansion() throws PropertyException + { + System.setProperty("banana", "fruity"); + System.setProperty("concrete", "horrible"); + String expandedProperty = PropertyUtils.replaceProperties("${banana}xyz${concrete}"); + assertEquals(expandedProperty, "fruityxyzhorrible"); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TestPropertyUtils.class); + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java new file mode 100644 index 0000000000..8f743d8856 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; + + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.commons.configuration.HierarchicalConfiguration.Node; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.queue.AMQPriorityQueue; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import junit.framework.TestCase; + +public class VirtualHostConfigurationTest extends TestCase +{ + + private File configFile; + private VirtualHostConfiguration vhostConfig; + private XMLConfiguration configXml; + + @Override + protected void setUp() throws Exception + { + // Create temporary configuration file + configFile = File.createTempFile(this.getName()+"config", ".xml"); + configFile.deleteOnExit(); + + // Fill config file with stuff + configXml = new XMLConfiguration(); + configXml.setRootElementName("virtualhosts"); + configXml.addProperty("virtualhost(-1).name", "test"); + } + + public void testQueuePriority() throws ConfigurationException, AMQException + { + // Set up queue with 5 priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "atest"); + configXml.addProperty("virtualhost.test.queues.queue.atest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.atest.priorities", + "5"); + + // Set up queue with JMS style priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "ptest"); + configXml.addProperty("virtualhost.test.queues.queue.ptest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.ptest.priority", + "true"); + + // Set up queue with no priorities + configXml.addProperty("virtualhost.test.queues(-1).queue(-1).name(-1)", + "ntest"); + configXml.addProperty("virtualhost.test.queues.queue.ntest(-1).exchange", + "amq.direct"); + configXml.addProperty("virtualhost.test.queues.queue.ntest.priority", + "false"); + configXml.save(configFile); + + // Setup virtual host configuration + vhostConfig = new VirtualHostConfiguration(configFile.getAbsolutePath()); + + // Do bindings and get resulting vhost + vhostConfig.performBindings(); + VirtualHost vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); + + // Check that atest was a priority queue with 5 priorities + AMQQueue atest = vhost.getQueueRegistry().getQueue(new AMQShortString("atest")); + assertTrue(atest instanceof AMQPriorityQueue); + assertEquals(5, ((AMQPriorityQueue) atest).getPriorities()); + + // Check that ptest was a priority queue with 10 priorities + AMQQueue ptest = vhost.getQueueRegistry().getQueue(new AMQShortString("ptest")); + assertTrue(ptest instanceof AMQPriorityQueue); + assertEquals(10, ((AMQPriorityQueue) ptest).getPriorities()); + + // Check that ntest wasn't a priority queue + AMQQueue ntest = vhost.getQueueRegistry().getQueue(new AMQShortString("ntest")); + assertFalse(ntest instanceof AMQPriorityQueue); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java new file mode 100644 index 0000000000..aa25e207a9 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java @@ -0,0 +1,595 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; +import junit.framework.Assert; +import org.apache.qpid.server.queue.*; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +import java.util.LinkedList; + +public class DestWildExchangeTest extends TestCase +{ + + TopicExchange _exchange; + + VirtualHost _vhost; + MessageStore _store; + StoreContext _context; + + InternalTestProtocolSession _protocolSession; + + + public void setUp() throws AMQException + { + _exchange = new TopicExchange(); + _vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + _store = new MemoryMessageStore(); + _context = new StoreContext(); + _protocolSession = new InternalTestProtocolSession(); + } + + public void tearDown() + { + ApplicationRegistry.remove(1); + } + + + public void testNoRoute() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + MessagePublishInfo info = new PublishInfo(new AMQShortString("a.b")); + + IncomingMessage message = new IncomingMessage(0L, info, null, _protocolSession); + + _exchange.route(message); + + Assert.assertEquals(0, queue.getMessageCount()); + } + + public void testDirectMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + + public void testStarMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*"), queue, null); + + + IncomingMessage message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + public void testHashMatch() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.#"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("b"); + + try + { + routeMessage(message); + fail("Message has no route and should fail to be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + + public void testMidHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + IncomingMessage message = createMessage("a.c.d.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.c.b"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMatchafterHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.a.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.b.c.b"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.b.c.b.c"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + + public void testHashAfterHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.a.b.c.d"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testHashHash() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null); + + + IncomingMessage message = createMessage("a.c.b.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.a.b.c.d"); + + try + { + routeMessage(message); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId()); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testSubMatchFails() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + private void routeMessage(final IncomingMessage message) + throws AMQException + { + _exchange.route(message); + message.routingComplete(_store, new MessageHandleFactory()); + message.deliverToQueues(); + } + + public void testMoreRouting() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a.b.c"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMoreQueue() throws AMQException + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + IncomingMessage message = createMessage("a"); + + try + { + routeMessage(message); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + private IncomingMessage createMessage(String s) throws AMQException + { + MessagePublishInfo info = new PublishInfo(new AMQShortString(s)); + + TransactionalContext trancontext = new NonTransactionalContext(_store, _context, null, + new LinkedList() + ); + + IncomingMessage message = new IncomingMessage(0L, info, trancontext,_protocolSession); + message.setContentHeaderBody( new ContentHeaderBody()); + + + return message; + } + + + class PublishInfo implements MessagePublishInfo + { + AMQShortString _routingkey; + + PublishInfo(AMQShortString routingkey) + { + _routingkey = routingkey; + } + + public AMQShortString getExchange() + { + return null; + } + + public void setExchange(AMQShortString exchange) + { + + } + + public boolean isImmediate() + { + return false; + } + + public boolean isMandatory() + { + return true; + } + + public AMQShortString getRoutingKey() + { + return _routingkey; + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java new file mode 100644 index 0000000000..8ce7b4c0e1 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java @@ -0,0 +1,145 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import junit.framework.TestCase; +import org.apache.qpid.server.queue.QueueRegistry; +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.registry.IApplicationRegistry; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.management.openmbean.TabularData; +import java.util.ArrayList; + +/** + * Unit test class for testing different Exchange MBean operations + */ +public class ExchangeMBeanTest extends TestCase +{ + private AMQQueue _queue; + private QueueRegistry _queueRegistry; + private VirtualHost _virtualHost; + + /** + * Test for direct exchange mbean + * @throws Exception + */ + + public void testDirectExchangeMBean() throws Exception + { + DirectExchange exchange = new DirectExchange(); + exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + mbean.createNewBinding(_queue.getName().toString(), "binding1"); + mbean.createNewBinding(_queue.getName().toString(), "binding2"); + + TabularData data = mbean.bindings(); + ArrayList list = new ArrayList(data.values()); + assertTrue(list.size() == 2); + + // test general exchange properties + assertEquals(mbean.getName(), "amq.direct"); + assertEquals(mbean.getExchangeType(), "direct"); + assertTrue(mbean.getTicketNo() == 0); + assertTrue(!mbean.isDurable()); + assertTrue(mbean.isAutoDelete()); + } + + /** + * Test for "topic" exchange mbean + * @throws Exception + */ + + public void testTopicExchangeMBean() throws Exception + { + TopicExchange exchange = new TopicExchange(); + exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + mbean.createNewBinding(_queue.getName().toString(), "binding1"); + mbean.createNewBinding(_queue.getName().toString(), "binding2"); + + TabularData data = mbean.bindings(); + ArrayList list = new ArrayList(data.values()); + assertTrue(list.size() == 2); + + // test general exchange properties + assertEquals(mbean.getName(), "amq.topic"); + assertEquals(mbean.getExchangeType(), "topic"); + assertTrue(mbean.getTicketNo() == 0); + assertTrue(!mbean.isDurable()); + assertTrue(mbean.isAutoDelete()); + } + + /** + * Test for "Headers" exchange mbean + * @throws Exception + */ + + public void testHeadersExchangeMBean() throws Exception + { + HeadersExchange exchange = new HeadersExchange(); + exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + ManagedObject managedObj = exchange.getManagedObject(); + ManagedExchange mbean = (ManagedExchange)managedObj; + + mbean.createNewBinding(_queue.getName().toString(), "key1=binding1,key2=binding2"); + mbean.createNewBinding(_queue.getName().toString(), "key3=binding3"); + + TabularData data = mbean.bindings(); + ArrayList list = new ArrayList(data.values()); + assertTrue(list.size() == 2); + + // test general exchange properties + assertEquals(mbean.getName(), "amq.match"); + assertEquals(mbean.getExchangeType(), "headers"); + assertTrue(mbean.getTicketNo() == 0); + assertTrue(!mbean.isDurable()); + assertTrue(mbean.isAutoDelete()); + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _queueRegistry = _virtualHost.getQueueRegistry(); + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost, + null); + _queueRegistry.registerQueue(_queue); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java new file mode 100644 index 0000000000..86ba96bf5d --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.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.exchange; + +import java.util.Map; +import java.util.HashMap; + +import junit.framework.TestCase; +import org.apache.qpid.framing.FieldTable; + +/** + */ +public class HeadersBindingTest extends TestCase +{ + private FieldTable bindHeaders = new FieldTable(); + private FieldTable matchHeaders = new FieldTable(); + + public void testDefault_1() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testDefault_2() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testDefault_3() + { + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Altered value of A"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_1() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_2() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_3() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_4() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAll_5() + { + bindHeaders.setString("X-match", "all"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_1() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_2() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_3() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_4() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_5() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public void testAny_6() + { + bindHeaders.setString("X-match", "any"); + bindHeaders.setString("A", "Value of A"); + bindHeaders.setString("B", "Value of B"); + + matchHeaders.setString("A", "Altered value of A"); + matchHeaders.setString("B", "Altered value of B"); + matchHeaders.setString("C", "Value of C"); + + assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders)); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(HeadersBindingTest.class); + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java new file mode 100644 index 0000000000..da35ddc594 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -0,0 +1,180 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.AMQChannel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +public class InternalTestProtocolSession extends AMQMinaProtocolSession implements ProtocolOutputConverter +{ + // ChannelID(LIST) -> LinkedList + final Map>> _channelDelivers; + private AtomicInteger _deliveryCount = new AtomicInteger(0); + + public InternalTestProtocolSession() throws AMQException + { + super(new TestIoSession(), + ApplicationRegistry.getInstance().getVirtualHostRegistry(), + new AMQCodecFactory(true)); + + _channelDelivers = new HashMap>>(); + + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return this; + } + + public byte getProtocolMajorVersion() + { + return (byte) 8; + } + + public byte getProtocolMinorVersion() + { + return (byte) 0; + } + + // *** + + public List getDelivers(int channelId, AMQShortString consumerTag, int count) + { + synchronized (_channelDelivers) + { + List all =_channelDelivers.get(channelId).get(consumerTag); + + if (all == null) + { + return new ArrayList(0); + } + + List msgs = all.subList(0, count); + + List response = new ArrayList(msgs); + + //Remove the msgs from the receivedList. + msgs.clear(); + + return response; + } + } + + // *** ProtocolOutputConverter Implementation + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + } + + public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag) + { + } + + public void writeDeliver(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) throws AMQException + { + _deliveryCount.incrementAndGet(); + + synchronized (_channelDelivers) + { + Map> consumers = _channelDelivers.get(channelId); + + if (consumers == null) + { + consumers = new HashMap>(); + _channelDelivers.put(channelId, consumers); + } + + LinkedList consumerDelivers = consumers.get(consumerTag); + + if (consumerDelivers == null) + { + consumerDelivers = new LinkedList(); + consumers.put(consumerTag, consumerDelivers); + } + + consumerDelivers.add(new DeliveryPair(deliveryTag, message)); + } + } + + public void writeGetOk(AMQMessage message, int channelId, long deliveryTag, int queueSize) throws AMQException + { + } + + public void awaitDelivery(int msgs) + { + while (msgs > _deliveryCount.get()) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + public class DeliveryPair + { + private long _deliveryTag; + private AMQMessage _message; + + public DeliveryPair(long deliveryTag, AMQMessage message) + { + _deliveryTag = deliveryTag; + _message = message; + } + + public AMQMessage getMessage() + { + return _message; + } + + public long getDeliveryTag() + { + return _deliveryTag; + } + } + + public boolean isClosed() + { + return _closed; + } + + public void closeProtocolSession(boolean waitLast) + { + // Override as we don't have a real IOSession to close. + // The alternative is to fully implement the TestIOSession to return a CloseFuture from close(); + // Then the AMQMinaProtocolSession can join on the returning future without a NPE. + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java new file mode 100644 index 0000000000..ff4d3ed9fb --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java @@ -0,0 +1,295 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.mina.common.*; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.qpid.pool.ReadWriteThreadModel; + +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Test implementation of IoSession, which is required for some tests. Methods not being used are not implemented, + * so if this class is being used and some methods are to be used, then please update those. + */ +public class TestIoSession implements IoSession +{ + private final ConcurrentMap attributes = new ConcurrentHashMap(); + + public TestIoSession() + { + } + + public IoService getService() + { + return null; + } + + public IoServiceConfig getServiceConfig() + { + return new TestIoConfig(); + } + + public IoHandler getHandler() + { + return null; + } + + public IoSessionConfig getConfig() + { + return null; + } + + public IoFilterChain getFilterChain() + { + return null; + } + + public WriteFuture write(Object message) + { + return null; + } + + public CloseFuture close() + { + return null; + } + + public Object getAttachment() + { + return getAttribute(""); + } + + public Object setAttachment(Object attachment) + { + return setAttribute("",attachment); + } + + public Object getAttribute(String key) + { + return attributes.get(key); + } + + public Object setAttribute(String key, Object value) + { + return attributes.put(key,value); + } + + public Object setAttribute(String key) + { + return attributes.put(key, Boolean.TRUE); + } + + public Object removeAttribute(String key) + { + return attributes.remove(key); + } + + public boolean containsAttribute(String key) + { + return attributes.containsKey(key); + } + + public Set getAttributeKeys() + { + return attributes.keySet(); + } + + public TransportType getTransportType() + { + return null; + } + + public boolean isConnected() + { + return false; + } + + public boolean isClosing() + { + return false; + } + + public CloseFuture getCloseFuture() + { + return null; + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress("127.0.0.1", 1234); + } + + public SocketAddress getLocalAddress() + { + return null; + } + + public SocketAddress getServiceAddress() + { + return null; + } + + public int getIdleTime(IdleStatus status) + { + return 0; + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + + } + + public int getWriteTimeout() + { + return 0; + } + + public long getWriteTimeoutInMillis() + { + return 0; + } + + public void setWriteTimeout(int writeTimeout) + { + + } + + public TrafficMask getTrafficMask() + { + return null; + } + + public void setTrafficMask(TrafficMask trafficMask) + { + + } + + public void suspendRead() + { + + } + + public void suspendWrite() + { + + } + + public void resumeRead() + { + + } + + public void resumeWrite() + { + + } + + public long getReadBytes() + { + return 0; + } + + public long getWrittenBytes() + { + return 0; + } + + public long getReadMessages() + { + return 0; + } + + public long getWrittenMessages() + { + return 0; + } + + public long getWrittenWriteRequests() + { + return 0; + } + + public int getScheduledWriteRequests() + { + return 0; + } + + public int getScheduledWriteBytes() + { + return 0; + } + + public long getCreationTime() + { + return 0; + } + + public long getLastIoTime() + { + return 0; + } + + public long getLastReadTime() + { + return 0; + } + + public long getLastWriteTime() + { + return 0; + } + + public boolean isIdle(IdleStatus status) + { + return false; + } + + public int getIdleCount(IdleStatus status) + { + return 0; + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; + } + + /** + * Test implementation of IoServiceConfig + */ + private class TestIoConfig extends SocketAcceptorConfig + { + public ThreadModel getThreadModel() + { + return ReadWriteThreadModel.getInstance(); + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java new file mode 100644 index 0000000000..aff7af6952 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java @@ -0,0 +1,107 @@ +package org.apache.qpid.server.queue; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +import java.util.ArrayList; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import junit.framework.AssertionFailedError; + +public class AMQPriorityQueueTest extends SimpleAMQQueueTest +{ + + @Override + protected void setUp() throws Exception + { + _arguments = new FieldTable(); + _arguments.put(AMQQueueFactory.X_QPID_PRIORITIES, 3); + super.setUp(); + } + + public void testPriorityOrdering() throws AMQException, InterruptedException + { + + // Enqueue messages in order + _queue.enqueue(null, createMessage(1L, (byte) 10)); + _queue.enqueue(null, createMessage(2L, (byte) 4)); + _queue.enqueue(null, createMessage(3L, (byte) 0)); + + // Enqueue messages in reverse order + _queue.enqueue(null, createMessage(4L, (byte) 0)); + _queue.enqueue(null, createMessage(5L, (byte) 4)); + _queue.enqueue(null, createMessage(6L, (byte) 10)); + + // Enqueue messages out of order + _queue.enqueue(null, createMessage(7L, (byte) 4)); + _queue.enqueue(null, createMessage(8L, (byte) 10)); + _queue.enqueue(null, createMessage(9L, (byte) 0)); + + // Register subscriber + _queue.registerSubscription(_subscription, false); + Thread.sleep(150); + + ArrayList msgs = _subscription.getMessages(); + try + { + assertEquals(new Long(1L), msgs.get(0).getMessage().getMessageId()); + assertEquals(new Long(6L), msgs.get(1).getMessage().getMessageId()); + assertEquals(new Long(8L), msgs.get(2).getMessage().getMessageId()); + + assertEquals(new Long(2L), msgs.get(3).getMessage().getMessageId()); + assertEquals(new Long(5L), msgs.get(4).getMessage().getMessageId()); + assertEquals(new Long(7L), msgs.get(5).getMessage().getMessageId()); + + assertEquals(new Long(3L), msgs.get(6).getMessage().getMessageId()); + assertEquals(new Long(4L), msgs.get(7).getMessage().getMessageId()); + assertEquals(new Long(9L), msgs.get(8).getMessage().getMessageId()); + } + catch (AssertionFailedError afe) + { + // Show message order on failure. + int index = 1; + for (QueueEntry qe : msgs) + { + System.err.println(index + ":" + qe.getMessage().getMessageId()); + index++; + } + + throw afe; + } + + } + + protected AMQMessage createMessage(Long id, byte i) throws AMQException + { + AMQMessage msg = super.createMessage(id); + BasicContentHeaderProperties props = new BasicContentHeaderProperties(); + props.setPriority(i); + msg.getContentHeaderBody().properties = props; + return msg; + } + + protected AMQMessage createMessage(Long id) throws AMQException + { + return createMessage(id, (byte) 0); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java new file mode 100644 index 0000000000..0ada9cefee --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -0,0 +1,377 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.mina.common.ByteBuffer; + +import javax.management.Notification; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; +import java.util.Set; + +/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ +public class AMQQueueAlertTest extends TestCase +{ + 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 AMQQueue _queue; + private AMQQueueMBean _queueMBean; + private VirtualHost _virtualHost; + private AMQMinaProtocolSession _protocolSession; + private MessageStore _messageStore = new MemoryMessageStore(); + private StoreContext _storeContext = new StoreContext(); + private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList() + ); + 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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE); + + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH); + + while (_queue.getQueueDepth() < MAX_QUEUE_DEPTH) + { + sendMessages(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 + { + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost, + null); + _queueMBean = (AMQQueueMBean) _queue.getManagedObject(); + _queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT); + _queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE); + + sendMessages(1, MAX_MESSAGE_SIZE); + + // Ensure message sits on queue long enough to age. + Thread.sleep(MAX_MESSAGE_AGE * 2); + + sendMessages(1, MAX_MESSAGE_SIZE); + assertTrue(_queueMBean.getMessageCount() == 2); + + Notification lastNotification = _queueMBean.getLastNotification(); + assertNotNull(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 + { + _protocolSession = new InternalTestProtocolSession(); + AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore); + _protocolSession.addChannel(channel); + + // Create queue + _queue = getNewQueue(); + Subscription subscription = + SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); + + _queue.registerSubscription( + subscription, false); + + _queueMBean = (AMQQueueMBean) _queue.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) >> 10; + sendMessages(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 + _queue.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(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager()); + + _queue.registerSubscription( + subscription2, false); + + while (_queue.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. + _queue.unregisterSubscription(subscription2); + channel.requeue(); + + assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth())); + _protocolSession.closeSession(); + + // Check the clear queue + _queueMBean.clearQueue(); + assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth())); + } + + public void testAlertConfiguration() throws AMQException + { + // Setup configuration + CompositeConfiguration config = new CompositeConfiguration(); + config.setProperty("maximumMessageSize", new Long(23)); + config.setProperty("maximumMessageCount", new Long(24)); + config.setProperty("maximumQueueDepth", new Long(25)); + config.setProperty("maximumMessageAge", new Long(26)); + + // Create queue and set config + _queue = getNewQueue(); + _queue.configure(config); + + // Check alerts and notifications + Set checks = _queue.getNotificationChecks(); + assertNotNull("No checks found", checks); + assertFalse("Checks should not be empty", checks.isEmpty()); + assertEquals("Wrong number of checks", 4, checks.size()); + } + + 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(); + contentHeaderBody.bodySize = size; // in bytes + IncomingMessage message = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession); + message.setContentHeaderBody(contentHeaderBody); + + return message; + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _protocolSession = new InternalTestProtocolSession(); + + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + + private void sendMessages(long messageCount, final long size) throws AMQException + { + IncomingMessage[] messages = new IncomingMessage[(int) messageCount]; + for (int i = 0; i < messages.length; i++) + { + messages[i] = message(false, size); + ArrayList qs = new ArrayList(); + qs.add(_queue); + messages[i].enqueue(qs); + messages[i].routingComplete(_messageStore, new MessageHandleFactory()); + + } + + for (int i = 0; i < messageCount; i++) + { + messages[i].addContentBodyFrame(new ContentChunk(){ + + ByteBuffer _data = ByteBuffer.allocate((int)size); + + public int getSize() + { + return (int) size; + } + + public ByteBuffer getData() + { + return _data; + } + + public void reduceToFit() + { + + } + }); + messages[i].deliverToQueues(); + } + } + + private AMQQueue getNewQueue() throws AMQException + { + return AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue" + Math.random()), + false, + new AMQShortString("AMQueueAlertTest"), + false, + _virtualHost, null); + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java new file mode 100644 index 0000000000..520e49c56a --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java @@ -0,0 +1,85 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import junit.framework.TestCase; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +public class AMQQueueFactoryTest extends TestCase +{ + QueueRegistry _queueRegistry; + VirtualHost _virtualHost; + + public void setUp() + { + ApplicationRegistry registry = (ApplicationRegistry) ApplicationRegistry.getInstance(); + + _virtualHost = registry.getVirtualHostRegistry().getVirtualHost("test"); + + _queueRegistry = _virtualHost.getQueueRegistry(); + + assertEquals("Queues registered on an empty virtualhost", 0, _queueRegistry.getQueues().size()); + } + + public void tearDown() + { + assertEquals("Queue was not registered in virtualhost", 1, _queueRegistry.getQueues().size()); + ApplicationRegistry.remove(1); + } + + + public void testPriorityQueueRegistration() + { + FieldTable fieldTable = new FieldTable(); + fieldTable.put(new AMQShortString(AMQQueueFactory.X_QPID_PRIORITIES), 5); + + try + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testPriorityQueue"), false, new AMQShortString("owner"), false, + _virtualHost, fieldTable); + + assertEquals("Queue not a priorty queue", AMQPriorityQueue.class, queue.getClass()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + + + public void testSimpleQueueRegistration() + { + try + { + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("owner"), false, + _virtualHost, null); + assertEquals("Queue not a simple queue", SimpleAMQQueue.class, queue.getClass()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java new file mode 100644 index 0000000000..eab8ad3e2e --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -0,0 +1,349 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.RequiredDeliveryException; +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.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.txn.TransactionalContext; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.MemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.mina.common.ByteBuffer; + +import javax.management.JMException; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Collections; + +/** + * Test class to test AMQQueueMBean attribtues and operations + */ +public class AMQQueueMBeanTest extends TestCase +{ + private static long MESSAGE_SIZE = 1000; + private AMQQueue _queue; + private AMQQueueMBean _queueMBean; + private MessageStore _messageStore; + private StoreContext _storeContext = new StoreContext(); + private TransactionalContext _transactionalContext; + private VirtualHost _virtualHost; + private AMQProtocolSession _protocolSession; + 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) >> 10; + 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) >> 10; + 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(); + } + + // todo: collect to a general testing class -duplicated from Systest/MessageReturntest + private void verifyBrokerState() + { + + TestableMemoryMessageStore store = new TestableMemoryMessageStore((MemoryMessageStore) _virtualHost.getMessageStore()); + + // Unlike MessageReturnTest there is no need for a delay as there this thread does the clean up. + assertNotNull("ContentBodyMap should not be null", store.getContentBodyMap()); + assertEquals("Expected the store to have no content:" + store.getContentBodyMap(), 0, store.getContentBodyMap().size()); + assertNotNull("MessageMetaDataMap should not be null", store.getMessageMetaDataMap()); + assertEquals("Expected the store to have no metadata:" + store.getMessageMetaDataMap(), 0, store.getMessageMetaDataMap().size()); + } + + public void testConsumerCount() throws AMQException + { + + assertTrue(_queue.getActiveConsumerCount() == 0); + assertTrue(_queueMBean.getActiveConsumerCount() == 0); + + + InternalTestProtocolSession protocolSession = new InternalTestProtocolSession(); + AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore); + protocolSession.addChannel(channel); + + Subscription subscription = + SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), protocolSession, new AMQShortString("test"), false, null, false, channel.getCreditManager()); + + _queue.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()); + _queue.registerSubscription(s1,false); + _queue.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() + { + long maxQueueDepth = 1000; // in bytes + _queueMBean.setMaximumMessageCount(50000l); + _queueMBean.setMaximumMessageSize(2000l); + _queueMBean.setMaximumQueueDepth(maxQueueDepth); + + assertTrue(_queueMBean.getMaximumMessageCount() == 50000); + assertTrue(_queueMBean.getMaximumMessageSize() == 2000); + assertTrue(_queueMBean.getMaximumQueueDepth() == (maxQueueDepth >> 10)); + + assertTrue(_queueMBean.getName().equals("testQueue")); + assertTrue(_queueMBean.getOwner().equals("AMQueueMBeanTest")); + assertFalse(_queueMBean.isAutoDelete()); + assertFalse(_queueMBean.isDurable()); + } + + public void testExceptions() throws Exception + { + try + { + _queueMBean.viewMessages(0, 3); + fail(); + } + catch (JMException ex) + { + + } + + try + { + _queueMBean.viewMessages(2, 1); + fail(); + } + catch (JMException ex) + { + + } + + try + { + _queueMBean.viewMessages(-1, 1); + fail(); + } + catch (JMException ex) + { + + } + + IncomingMessage msg = message(false, false); + long id = msg.getMessageId(); + _queue.clearQueue(_storeContext); + ArrayList qs = new ArrayList(); + qs.add(_queue); + msg.enqueue(qs); + msg.routingComplete(_messageStore, new MessageHandleFactory()); + + msg.addContentBodyFrame(new ContentChunk() + { + ByteBuffer _data = ByteBuffer.allocate((int)MESSAGE_SIZE); + + public int getSize() + { + return (int) MESSAGE_SIZE; + } + + public ByteBuffer getData() + { + return _data; + } + + public void reduceToFit() + { + + } + }); + msg.deliverToQueues(); +// _queue.process(_storeContext, new QueueEntry(_queue, msg), false); + _queueMBean.viewMessageContent(id); + try + { + _queueMBean.viewMessageContent(id + 1); + fail(); + } + catch (JMException ex) + { + + } + } + + private IncomingMessage message(final boolean immediate, boolean persistent) 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(); + contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes + contentHeaderBody.properties = new BasicContentHeaderProperties(); + ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) (persistent ? 2 : 1)); + IncomingMessage msg = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession); + msg.setContentHeaderBody(contentHeaderBody); + return msg; + + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(1); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _messageStore = _virtualHost.getMessageStore(); + + _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList() + ); + + _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost, + null); + _queueMBean = new AMQQueueMBean(_queue); + + _protocolSession = new InternalTestProtocolSession(); + } + + public void tearDown() + { + ApplicationRegistry.remove(1); + } + + private void sendMessages(int messageCount, boolean persistent) throws AMQException + { + for (int i = 0; i < messageCount; i++) + { + IncomingMessage currentMessage = message(false, persistent); + ArrayList qs = new ArrayList(); + qs.add(_queue); + currentMessage.enqueue(qs); + + // route header + currentMessage.routingComplete(_messageStore, new MessageHandleFactory()); + + // Add the body so we have somthing to test later + currentMessage.addContentBodyFrame( + _protocolSession.getMethodRegistry() + .getProtocolVersionMethodConverter() + .convertToContentChunk( + new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE), + MESSAGE_SIZE))); + currentMessage.deliverToQueues(); + + + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java new file mode 100644 index 0000000000..355ba6a362 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessage.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +public class MockAMQMessage extends AMQMessage +{ + public MockAMQMessage(long messageId) + throws AMQException + { + super(new MockAMQMessageHandle(messageId) , + (StoreContext)null, + (MessagePublishInfo)new MockMessagePublishInfo()); + } + + protected MockAMQMessage(AMQMessage msg) + throws AMQException + { + super(msg); + } + + + @Override + public long getSize() + { + return 0l; + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.java new file mode 100644 index 0000000000..bdb0707c27 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQMessageHandle.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.queue; + +import org.apache.qpid.server.store.StoreContext; + +public class MockAMQMessageHandle extends InMemoryMessageHandle +{ + public MockAMQMessageHandle(final Long messageId) + { + super(messageId); + } + + @Override + public long getBodySize(StoreContext store) + { + return 0l; + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java new file mode 100644 index 0000000000..cecb430574 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -0,0 +1,313 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.AMQException; +import org.apache.commons.configuration.Configuration; + +import java.util.List; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; +import java.util.LinkedList; + +public class MockAMQQueue implements AMQQueue +{ + private boolean _deleted = false; + + public AMQShortString getName() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isDurable() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isAutoDelete() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public AMQShortString getOwner() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public VirtualHost getVirtualHost() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public List getExchangeBindings() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void registerSubscription(Subscription subscription, boolean exclusive) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void unregisterSubscription(Subscription subscription) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public int getConsumerCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getActiveConsumerCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isUnused() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isEmpty() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getMessageCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getUndeliveredMessageCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getQueueDepth() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getReceivedMessageCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getOldestMessageArrivalTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isDeleted() + { + return _deleted; + } + + public int delete() throws AMQException + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean resend(QueueEntry entry, Subscription subscription) throws AMQException + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public void addQueueDeleteTask(Task task) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public List getMessagesOnTheQueue() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public List getMessagesOnTheQueue(long fromMessageId, long toMessageId) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public List getMessagesOnTheQueue(int num) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public List getMessagesOnTheQueue(int num, int offest) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public QueueEntry getMessageOnTheQueue(long messageId) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getMaximumMessageSize() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setMaximumMessageSize(long value) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getMaximumMessageCount() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setMaximumMessageCount(long value) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getMaximumQueueDepth() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setMaximumQueueDepth(long value) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getMaximumMessageAge() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setMaximumMessageAge(long maximumMessageAge) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getMinimumAlertRepeatGap() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void deleteMessageFromTop(StoreContext storeContext) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long clearQueue(StoreContext storeContext) throws AMQException + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void removeExpiredIfNoSubscribers() throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public Set getNotificationChecks() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void flushSubscription(Subscription sub) throws AMQException + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void deliverAsync(Subscription sub) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void deliverAsync() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void stop() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void configure(Configuration virtualHostDefaultQueueConfiguration) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public ManagedObject getManagedObject() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public int compareTo(AMQQueue o) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.java new file mode 100644 index 0000000000..5a5ffaa14d --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockMessagePublishInfo.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.queue; + +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.AMQShortString; + +public class MockMessagePublishInfo implements MessagePublishInfo +{ + public AMQShortString getExchange() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setExchange(AMQShortString exchange) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isImmediate() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isMandatory() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public AMQShortString getRoutingKey() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java new file mode 100644 index 0000000000..37f91e7464 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java @@ -0,0 +1,197 @@ +/* +* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.subscription.Subscription; + +public class MockQueueEntry implements QueueEntry +{ + + private AMQMessage _message; + + public boolean acquire() + { + return false; + } + + public boolean acquire(Subscription sub) + { + return false; + } + + public boolean acquiredBySubscription() + { + return false; + } + + public void addStateChangeListener(StateChangeListener listener) + { + + } + + public String debugIdentity() + { + return null; + } + + public boolean delete() + { + return false; + } + + public void dequeue(StoreContext storeContext) throws FailedDequeueException + { + + } + + public void discard(StoreContext storeContext) throws FailedDequeueException, MessageCleanupException + { + + } + + public void dispose(StoreContext storeContext) throws MessageCleanupException + { + + } + + public boolean expired() throws AMQException + { + return false; + } + + public Subscription getDeliveredSubscription() + { + return null; + } + + public boolean getDeliveredToConsumer() + { + return false; + } + + public AMQMessage getMessage() + { + return _message; + } + + public AMQQueue getQueue() + { + return null; + } + + public long getSize() + { + return 0; + } + + public boolean immediateAndNotDelivered() + { + return false; + } + + public boolean isAcquired() + { + return false; + } + + public boolean isDeleted() + { + return false; + } + + + public boolean isQueueDeleted() + { + + return false; + } + + + public boolean isRejectedBy(Subscription subscription) + { + + return false; + } + + + public void reject() + { + + + } + + + public void reject(Subscription subscription) + { + + + } + + + public void release() + { + + + } + + + public boolean removeStateChangeListener(StateChangeListener listener) + { + + return false; + } + + + public void requeue(StoreContext storeContext) throws AMQException + { + + + } + + + public void setDeliveredToSubscription() + { + + + } + + + public void setRedelivered(boolean b) + { + + + } + + + public int compareTo(QueueEntry o) + { + + return 0; + } + + public void setMessage(AMQMessage msg) + { + _message = msg; + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java new file mode 100644 index 0000000000..7a60ed9795 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -0,0 +1,432 @@ +package org.apache.qpid.server.queue; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +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.ContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.exchange.DirectExchange; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.subscription.MockSubscription; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SimpleAMQQueueTest extends TestCase +{ + + protected SimpleAMQQueue _queue; + protected VirtualHost _virtualHost; + protected TestableMemoryMessageStore _store = new TestableMemoryMessageStore(); + protected AMQShortString _qname = new AMQShortString("qname"); + protected AMQShortString _owner = new AMQShortString("owner"); + protected AMQShortString _routingKey = new AMQShortString("routing key"); + protected DirectExchange _exchange = new DirectExchange(); + protected MockSubscription _subscription = new MockSubscription(); + protected FieldTable _arguments = null; + + MessagePublishInfo info = 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 false; + } + + public boolean isMandatory() + { + return false; + } + + public AMQShortString getRoutingKey() + { + return null; + } + }; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + //Create Application Registry for test + ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance(1); + + _virtualHost = new VirtualHost("vhost", _store); + applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost); + + _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, _virtualHost, _arguments); + } + + @Override + protected void tearDown() + { + _queue.stop(); + ApplicationRegistry.remove(1); + } + + public void testCreateQueue() throws AMQException + { + _queue.stop(); + try { + _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(null, false, _owner, false, _virtualHost, _arguments ); + assertNull("Queue was created", _queue); + } + catch (IllegalArgumentException e) + { + assertTrue("Exception was not about missing name", + e.getMessage().contains("name")); + } + + try { + _queue = new SimpleAMQQueue(_qname, false, _owner, false, null); + assertNull("Queue was created", _queue); + } + catch (IllegalArgumentException e) + { + assertTrue("Exception was not about missing vhost", + e.getMessage().contains("Host")); + } + + _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, + _virtualHost, _arguments); + assertNotNull("Queue was not created", _queue); + } + + public void testGetVirtualHost() + { + assertEquals("Virtual host was wrong", _virtualHost, _queue.getVirtualHost()); + } + + public void testBinding() + { + try + { + _queue.bind(_exchange, _routingKey, null); + assertTrue("Routing key was not bound", + _exchange.getBindings().containsKey(_routingKey)); + assertEquals("Queue was not bound to key", + _exchange.getBindings().get(_routingKey).get(0), + _queue); + assertEquals("Exchange binding count", 1, + _queue.getExchangeBindings().size()); + assertEquals("Wrong exchange bound", _routingKey, + _queue.getExchangeBindings().get(0).getRoutingKey()); + assertEquals("Wrong exchange bound", _exchange, + _queue.getExchangeBindings().get(0).getExchange()); + + _queue.unBind(_exchange, _routingKey, null); + assertFalse("Routing key was still bound", + _exchange.getBindings().containsKey(_routingKey)); + assertNull("Routing key was not empty", + _exchange.getBindings().get(_routingKey)); + } + catch (AMQException e) + { + assertNull("Unexpected exception", e); + } + } + + public void testSubscription() throws AMQException + { + // Check adding a subscription adds it to the queue + _queue.registerSubscription(_subscription, false); + assertEquals("Subscription did not get queue", _queue, + _subscription.getQueue()); + assertEquals("Queue does not have consumer", 1, + _queue.getConsumerCount()); + assertEquals("Queue does not have active consumer", 1, + _queue.getActiveConsumerCount()); + + // Check sending a message ends up with the subscriber + AMQMessage messageA = createMessage(new Long(24)); + _queue.enqueue(null, messageA); + assertEquals(messageA, _subscription.getLastSeenEntry().getMessage()); + + // Check removing the subscription removes it's information from the queue + _queue.unregisterSubscription(_subscription); + assertTrue("Subscription still had queue", _subscription.isClosed()); + assertFalse("Queue still has consumer", 1 == _queue.getConsumerCount()); + assertFalse("Queue still has active consumer", + 1 == _queue.getActiveConsumerCount()); + + AMQMessage messageB = createMessage(new Long (25)); + _queue.enqueue(null, messageB); + QueueEntry entry = _subscription.getLastSeenEntry(); + assertNull(entry); + } + + public void testQueueNoSubscriber() throws AMQException, InterruptedException + { + AMQMessage messageA = createMessage(new Long(24)); + _queue.enqueue(null, messageA); + _queue.registerSubscription(_subscription, false); + Thread.sleep(150); + assertEquals(messageA, _subscription.getLastSeenEntry().getMessage()); + } + + public void testExclusiveConsumer() throws AMQException + { + // Check adding an exclusive subscription adds it to the queue + _queue.registerSubscription(_subscription, true); + assertEquals("Subscription did not get queue", _queue, + _subscription.getQueue()); + assertEquals("Queue does not have consumer", 1, + _queue.getConsumerCount()); + assertEquals("Queue does not have active consumer", 1, + _queue.getActiveConsumerCount()); + + // Check sending a message ends up with the subscriber + AMQMessage messageA = createMessage(new Long(24)); + _queue.enqueue(null, messageA); + assertEquals(messageA, _subscription.getLastSeenEntry().getMessage()); + + // Check we cannot add a second subscriber to the queue + Subscription subB = new MockSubscription(); + Exception ex = null; + try + { + _queue.registerSubscription(subB, false); + } + catch (AMQException e) + { + ex = e; + } + assertNotNull(ex); + assertTrue(ex instanceof AMQException); + + // Check we cannot add an exclusive subscriber to a queue with an + // existing subscription + _queue.unregisterSubscription(_subscription); + _queue.registerSubscription(_subscription, false); + try + { + _queue.registerSubscription(subB, true); + } + catch (AMQException e) + { + ex = e; + } + assertNotNull(ex); + } + + public void testAutoDeleteQueue() throws Exception + { + _queue.stop(); + _queue = new SimpleAMQQueue(_qname, false, _owner, true, _virtualHost); + _queue.registerSubscription(_subscription, false); + AMQMessage message = createMessage(new Long(25)); + _queue.enqueue(null, message); + _queue.unregisterSubscription(_subscription); + assertTrue("Queue was not deleted when subscription was removed", + _queue.isDeleted()); + } + + public void testResend() throws Exception + { + _queue.registerSubscription(_subscription, false); + Long id = new Long(26); + AMQMessage message = createMessage(id); + _queue.enqueue(null, message); + QueueEntry entry = _subscription.getLastSeenEntry(); + entry.setRedelivered(true); + _queue.resend(entry, _subscription); + + } + + public void testGetFirstMessageId() throws Exception + { + // Create message + Long messageId = new Long(23); + AMQMessage message = createMessage(messageId); + + // Put message on queue + _queue.enqueue(null, message); + // Get message id + Long testmsgid = _queue.getMessagesOnTheQueue(1).get(0); + + // Check message id + assertEquals("Message ID was wrong", messageId, testmsgid); + } + + public void testGetFirstFiveMessageIds() throws Exception + { + for (int i = 0 ; i < 5; i++) + { + // Create message + Long messageId = new Long(i); + AMQMessage message = createMessage(messageId); + // Put message on queue + _queue.enqueue(null, message); + } + // Get message ids + List msgids = _queue.getMessagesOnTheQueue(5); + + // Check message id + for (int i = 0; i < 5; i++) + { + Long messageId = new Long(i); + assertEquals("Message ID was wrong", messageId, msgids.get(i)); + } + } + + public void testGetLastFiveMessageIds() throws Exception + { + for (int i = 0 ; i < 10; i++) + { + // Create message + Long messageId = new Long(i); + AMQMessage message = createMessage(messageId); + // Put message on queue + _queue.enqueue(null, message); + } + // Get message ids + List msgids = _queue.getMessagesOnTheQueue(5, 5); + + // Check message id + for (int i = 0; i < 5; i++) + { + Long messageId = new Long(i+5); + assertEquals("Message ID was wrong", messageId, msgids.get(i)); + } + } + + public void testEnqueueDequeueOfPersistentMessageToNonDurableQueue() throws AMQException + { + // Create IncomingMessage and nondurable queue + NonTransactionalContext txnContext = new NonTransactionalContext(_store, null, null, null); + IncomingMessage msg = new IncomingMessage(1L, info, txnContext, null); + ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); + contentHeaderBody.properties = new BasicContentHeaderProperties(); + ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) 2); + msg.setContentHeaderBody(contentHeaderBody); + ArrayList qs = new ArrayList(); + + // Send persistent message + qs.add(_queue); + msg.enqueue(qs); + msg.routingComplete(_store, new MessageHandleFactory()); + _store.storeMessageMetaData(null, new Long(1L), new MessageMetaData(info, contentHeaderBody, 1)); + + // Check that it is enqueued + AMQQueue data = _store.getMessages().get(1L); + assertNotNull(data); + + // Dequeue message + MockQueueEntry entry = new MockQueueEntry(); + AMQMessage amqmsg = new AMQMessage(1L, _store, new MessageHandleFactory(), txnContext); + + entry.setMessage(amqmsg); + _queue.dequeue(null, entry); + + // Check that it is dequeued + data = _store.getMessages().get(1L); + assertNull(data); + } + + + // FIXME: move this to somewhere useful + private static AMQMessageHandle createMessageHandle(final long messageId, final MessagePublishInfo publishBody) + { + final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, + null, + false); + try + { + amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(), + publishBody, + new ContentHeaderBody() + { + public int getSize() + { + return 1; + } + }); + } + catch (AMQException e) + { + // won't happen + } + + + return amqMessageHandle; + } + + public class TestMessage extends AMQMessage + { + private final long _tag; + private int _count; + + TestMessage(long tag, long messageId, MessagePublishInfo publishBody, StoreContext storeContext) + throws AMQException + { + super(createMessageHandle(messageId, publishBody), storeContext, publishBody); + _tag = tag; + } + + + public boolean incrementReference() + { + _count++; + return true; + } + + public void decrementReference(StoreContext context) + { + _count--; + } + + void assertCountEquals(int expected) + { + assertEquals("Wrong count for message with tag " + _tag, expected, _count); + } + } + + protected AMQMessage createMessage(Long id) throws AMQException + { + AMQMessage messageA = new TestMessage(id, id, info, new StoreContext()); + return messageA; + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.java new file mode 100644 index 0000000000..f76c652793 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueThreadPoolTest.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.queue; + +import junit.framework.TestCase; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.AMQException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimpleAMQQueueThreadPoolTest extends TestCase +{ + + public void test() throws AMQException + { + VirtualHost test = ApplicationRegistry.getInstance(1).getVirtualHostRegistry().getVirtualHost("test"); + + try + { + SimpleAMQQueue queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false, + new AMQShortString("owner"), + false, test, null); + + assertTrue("Creation did not start Pool.", !ReferenceCountingExecutorService.getInstance().getPool().isShutdown()); + + queue.stop(); + + assertEquals("References still exist", 0, ReferenceCountingExecutorService.getInstance().getReferenceCount()); + + assertTrue("Stop did not clean up.", ReferenceCountingExecutorService.getInstance().getPool().isShutdown()); + } + finally + { + ApplicationRegistry.remove(1); + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java new file mode 100644 index 0000000000..03fcfc31e9 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java @@ -0,0 +1,120 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.registry; + +import junit.framework.TestCase; +import org.apache.qpid.server.util.TestApplicationRegistry; + +import java.security.Security; +import java.security.Provider; +import java.util.List; +import java.util.LinkedList; + +/** + * QPID-1390 : Test to validate that the AuthenticationManger succesfully unregisters any new SASL providers when + * The ApplicationRegistry is closed. + * + * This should be expanded as QPID-1399 is implemented. + */ +public class ApplicationRegistryShutdownTest extends TestCase +{ + + ApplicationRegistry _registry; + + public void setUp() + { + _registry = new TestApplicationRegistry(); + } + + /** + * QPID-1399 : Ensure that the Authentiction manager unregisters any SASL providers created during + * ApplicationRegistry initialisation. + * + */ + public void testAuthenticationMangerCleansUp() + { + // Get default providers + Provider[] defaultProviders = Security.getProviders(); + + // Register new providers + try + { + _registry.initialise(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + // Get the providers after initialisation + Provider[] providersAfterInitialisation = Security.getProviders(); + + // Find the additions + List additions = new LinkedList(); + for (Provider afterInit : providersAfterInitialisation) + { + boolean found = false; + for (Provider defaultProvider : defaultProviders) + { + if (defaultProvider == afterInit) + { + found=true; + break; + } + } + + // Record added registies + if (!found) + { + additions.add(afterInit); + } + } + + // Not using isEmpty as that is not in Java 5 + assertTrue("No new SASL mechanisms added by initialisation.", additions.size() != 0 ); + + //Close the registry which will perform the close the AuthenticationManager + try + { + _registry.close(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + + //Validate that the SASL plugins have been removed. + Provider[] providersAfterClose = Security.getProviders(); + + assertTrue("No providers unregistered", providersAfterInitialisation.length > providersAfterClose.length); + + //Ensure that the additions are not still present after close(). + for (Provider afterClose : providersAfterClose) + { + assertFalse("Added provider not unregistered", additions.contains(afterClose)); + } + } + + + + + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java new file mode 100644 index 0000000000..f3c07d9eb2 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/access/management/AMQUserManagementMBeanTest.java @@ -0,0 +1,152 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.security.access.management; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; + +import junit.framework.TestCase; + +public class AMQUserManagementMBeanTest extends TestCase +{ + private Base64MD5PasswordFilePrincipalDatabase _database; + private AMQUserManagementMBean _amqumMBean; + + private static final String _QPID_HOME = System.getProperty("QPID_HOME"); + + private static final String USERNAME = "testuser"; + private static final String PASSWORD = "password"; + private static final String JMXRIGHTS = "admin"; + private static final String TEMP_PASSWORD_FILE_NAME = "tempPasswordFile.tmp"; + private static final String TEMP_JMXACCESS_FILE_NAME = "tempJMXAccessFile.tmp"; + + @Override + protected void setUp() throws Exception + { + assertNotNull("QPID_HOME not set", _QPID_HOME); + + _database = new Base64MD5PasswordFilePrincipalDatabase(); + _amqumMBean = new AMQUserManagementMBean(); + } + + @Override + protected void tearDown() throws Exception + { + File testFile = new File(_QPID_HOME + File.separator + TEMP_JMXACCESS_FILE_NAME + ".tmp"); + if (testFile.exists()) + { + testFile.delete(); + } + + testFile = new File(_QPID_HOME + File.separator + TEMP_JMXACCESS_FILE_NAME + ".old"); + if (testFile.exists()) + { + testFile.delete(); + } + + testFile = new File(_QPID_HOME + File.separator + TEMP_PASSWORD_FILE_NAME + ".tmp"); + if (testFile.exists()) + { + testFile.delete(); + } + + testFile = new File(_QPID_HOME + File.separator + TEMP_PASSWORD_FILE_NAME + ".old"); + if (testFile.exists()) + { + testFile.delete(); + } + } + + public void testDeleteUser() + { + loadTestPasswordFile(); + loadTestAccessFile(); + + boolean deleted = false; + + try + { + deleted = _amqumMBean.deleteUser(USERNAME); + } + catch(Exception e){ + fail("Unable to delete user: " + e.getMessage()); + } + + assertTrue(deleted); + } + + + // ============================ Utility methods ========================= + + private void loadTestPasswordFile() + { + try + { + File tempPasswordFile = new File(_QPID_HOME + File.separator + TEMP_PASSWORD_FILE_NAME); + if (tempPasswordFile.exists()) + { + tempPasswordFile.delete(); + } + tempPasswordFile.deleteOnExit(); + + BufferedWriter passwordWriter = new BufferedWriter(new FileWriter(tempPasswordFile)); + passwordWriter.write(USERNAME + ":" + PASSWORD); + passwordWriter.newLine(); + passwordWriter.flush(); + + _database.setPasswordFile(tempPasswordFile.toString()); + _amqumMBean.setPrincipalDatabase(_database); + } + catch (IOException e) + { + fail("Unable to create test password file: " + e.getMessage()); + } + } + + private void loadTestAccessFile() + { + try + { + File tempAccessFile = new File(_QPID_HOME + File.separator + TEMP_JMXACCESS_FILE_NAME); + if (tempAccessFile.exists()) + { + tempAccessFile.delete(); + } + tempAccessFile.deleteOnExit(); + + BufferedWriter accessWriter = new BufferedWriter(new FileWriter(tempAccessFile)); + accessWriter.write(USERNAME + "=" + JMXRIGHTS); + accessWriter.newLine(); + accessWriter.flush(); + + _amqumMBean.setAccessFile(tempAccessFile.toString()); + } + catch (Exception e) + { + fail("Unable to create test access file: " + e.getMessage()); + } + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java new file mode 100644 index 0000000000..b5034d9f5d --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java @@ -0,0 +1,337 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.database; + +import junit.framework.TestCase; + +import javax.security.auth.login.AccountNotFoundException; + +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.security.Principal; +import java.util.List; +import java.util.regex.Pattern; + +public class Base64MD5PasswordFilePrincipalDatabaseTest extends TestCase +{ + + private static final String TEST_COMMENT = "# Test Comment"; + private String USERNAME = "testUser"; + private String _username = this.getClass().getName()+"username"; + private char[] _password = "password".toCharArray(); + private Principal _principal = new UsernamePrincipal(_username); + private Base64MD5PasswordFilePrincipalDatabase _database; + private File _pwdFile; + + public void setUp() throws Exception + { + _database = new Base64MD5PasswordFilePrincipalDatabase(); + _pwdFile = File.createTempFile(this.getClass().getName(), "pwd"); + _pwdFile.deleteOnExit(); + _database.setPasswordFile(_pwdFile.getAbsolutePath()); + } + + private File createPasswordFile(int commentLines, int users) + { + try + { + File testFile = File.createTempFile("Base64MD5PDPDTest","tmp"); + testFile.deleteOnExit(); + + BufferedWriter writer = new BufferedWriter(new FileWriter(testFile)); + + for (int i = 0; i < commentLines; i++) + { + writer.write(TEST_COMMENT); + writer.newLine(); + } + + for (int i = 0; i < users; i++) + { + writer.write(USERNAME + i + ":Password"); + writer.newLine(); + } + + writer.flush(); + writer.close(); + + return testFile; + + } + catch (IOException e) + { + fail("Unable to create test password file." + e.getMessage()); + } + + return null; + } + + private void loadPasswordFile(File file) + { + try + { + _database.setPasswordFile(file.toString()); + } + catch (IOException e) + { + fail("Password File was not created." + e.getMessage()); + } + } + + /** **** Test Methods ************** */ + + public void testCreatePrincipal() + { + File testFile = createPasswordFile(1, 0); + + loadPasswordFile(testFile); + + final String CREATED_PASSWORD = "createdPassword"; + final String CREATED_USERNAME = "createdUser"; + + Principal principal = new Principal() + { + public String getName() + { + return CREATED_USERNAME; + } + }; + + assertTrue("New user not created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray())); + + loadPasswordFile(testFile); + + assertNotNull("Created User was not saved", _database.getUser(CREATED_USERNAME)); + + assertFalse("Duplicate user created.", _database.createPrincipal(principal, CREATED_PASSWORD.toCharArray())); + + testFile.delete(); + } + + public void testDeletePrincipal() + { + File testFile = createPasswordFile(1, 1); + + loadPasswordFile(testFile); + + Principal user = _database.getUser(USERNAME + "0"); + assertNotNull("Generated user not present.", user); + + try + { + _database.deletePrincipal(user); + } + catch (AccountNotFoundException e) + { + fail("User should be present" + e.getMessage()); + } + + try + { + _database.deletePrincipal(user); + fail("User should not be present"); + } + catch (AccountNotFoundException e) + { + //pass + } + + loadPasswordFile(testFile); + + try + { + _database.deletePrincipal(user); + fail("User should not be present"); + } + catch (AccountNotFoundException e) + { + //pass + } + + assertNull("Deleted user still present.", _database.getUser(USERNAME + "0")); + + testFile.delete(); + } + + public void testGetUsers() + { + int USER_COUNT = 10; + File testFile = createPasswordFile(1, USER_COUNT); + + loadPasswordFile(testFile); + + Principal user = _database.getUser("MISSING_USERNAME"); + assertNull("Missing user present.", user); + + List users = _database.getUsers(); + + assertNotNull("Users list is null.", users); + + assertEquals(USER_COUNT, users.size()); + + boolean[] verify = new boolean[USER_COUNT]; + for (int i = 0; i < USER_COUNT; i++) + { + Principal principal = users.get(i); + + assertNotNull("Generated user not present.", principal); + + String name = principal.getName(); + + int id = Integer.parseInt(name.substring(USERNAME.length())); + + assertFalse("Duplicated username retrieve", verify[id]); + verify[id] = true; + } + + for (int i = 0; i < USER_COUNT; i++) + { + assertTrue("User " + i + " missing", verify[i]); + } + + testFile.delete(); + } + + public void testUpdatePasswordIsSavedToFile() + { + + File testFile = createPasswordFile(1, 1); + + loadPasswordFile(testFile); + + Principal testUser = _database.getUser(USERNAME + "0"); + + assertNotNull(testUser); + + String NEW_PASSWORD = "NewPassword"; + String NEW_PASSWORD_HASH = "TmV3UGFzc3dvcmQ="; + try + { + _database.updatePassword(testUser, NEW_PASSWORD.toCharArray()); + } + catch (AccountNotFoundException e) + { + fail(e.toString()); + } + + try + { + BufferedReader reader = new BufferedReader(new FileReader(testFile)); + + assertTrue("File has no content", reader.ready()); + + assertEquals("Comment line has been corrupted.", TEST_COMMENT, reader.readLine()); + + assertTrue("File is missing user data.", reader.ready()); + + String userLine = reader.readLine(); + + String[] result = Pattern.compile(":").split(userLine); + + assertEquals("User line not complete '" + userLine + "'", 2, result.length); + + assertEquals("Username not correct,", USERNAME + "0", result[0]); + assertEquals("New Password not correct,", NEW_PASSWORD_HASH, result[1]); + + assertFalse("File has more content", reader.ready()); + + } + catch (IOException e) + { + fail("Unable to valdate file contents due to:" + e.getMessage()); + } + testFile.delete(); + } + + public void testSetPasswordWithMissingFile() + { + try + { + _database.setPasswordFile("DoesntExist"); + } + catch (FileNotFoundException fnfe) + { + assertTrue(fnfe.getMessage(), fnfe.getMessage().startsWith("Cannot find password file")); + } + catch (IOException e) + { + fail("Password File was not created." + e.getMessage()); + } + + } + + public void testSetPasswordWithReadOnlyFile() + { + + File testFile = createPasswordFile(0, 0); + + testFile.setReadOnly(); + + try + { + _database.setPasswordFile(testFile.toString()); + } + catch (FileNotFoundException fnfe) + { + assertTrue(fnfe.getMessage().startsWith("Cannot read password file ")); + } + catch (IOException e) + { + fail("Password File was not created." + e.getMessage()); + } + + testFile.delete(); + } + + public void testCreateUserPrincipal() throws IOException + { + _database.createPrincipal(_principal, _password); + Principal newPrincipal = _database.getUser(_username); + assertNotNull(newPrincipal); + assertEquals(_principal.getName(), newPrincipal.getName()); + } + + public void testVerifyPassword() throws IOException, AccountNotFoundException + { + testCreateUserPrincipal(); + //assertFalse(_pwdDB.verifyPassword(_username, null)); + assertFalse(_database.verifyPassword(_username, new char[]{})); + assertFalse(_database.verifyPassword(_username, "massword".toCharArray())); + assertTrue(_database.verifyPassword(_username, _password)); + } + + public void testUpdatePassword() throws IOException, AccountNotFoundException + { + testCreateUserPrincipal(); + char[] newPwd = "newpassword".toCharArray(); + _database.updatePassword(_principal, newPwd); + assertFalse(_database.verifyPassword(_username, _password)); + assertTrue(_database.verifyPassword(_username, newPwd)); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java new file mode 100644 index 0000000000..a7d951cb5b --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/HashedUserTest.java @@ -0,0 +1,95 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.database; + +import junit.framework.TestCase; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +import java.io.UnsupportedEncodingException; + +/* + Note User is mainly tested by Base64MD5PFPDTest this is just to catch the extra methods + */ +public class HashedUserTest extends TestCase +{ + + String USERNAME = "username"; + String PASSWORD = "password"; + String HASHED_PASSWORD = "cGFzc3dvcmQ="; + + public void testToLongArrayConstructor() + { + try + { + HashedUser user = new HashedUser(new String[]{USERNAME, PASSWORD, USERNAME}); + fail("Error expected"); + } + catch (IllegalArgumentException e) + { + assertEquals("User Data should be length 2, username, password", e.getMessage()); + } + catch (UnsupportedEncodingException e) + { + fail(e.getMessage()); + } + } + + public void testArrayConstructor() + { + try + { + HashedUser user = new HashedUser(new String[]{USERNAME, HASHED_PASSWORD}); + assertEquals("Username incorrect", USERNAME, user.getName()); + int index = 0; + + char[] hash = HASHED_PASSWORD.toCharArray(); + + try + { + for (byte c : user.getEncodedPassword()) + { + assertEquals("Password incorrect", hash[index], (char) c); + index++; + } + } + catch (Exception e) + { + fail(e.getMessage()); + } + + hash = PASSWORD.toCharArray(); + + index=0; + for (char c : user.getPassword()) + { + assertEquals("Password incorrect", hash[index], c); + index++; + } + + } + catch (UnsupportedEncodingException e) + { + fail(e.getMessage()); + } + } +} + diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java new file mode 100644 index 0000000000..f80413d4f8 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.security.auth.sasl; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; + +import junit.framework.TestCase; + +public abstract class SaslServerTestCase extends TestCase +{ + protected SaslServer server; + protected String username = "u"; + protected String password = "p"; + protected String notpassword = "a"; + protected PrincipalDatabase db = new TestPrincipalDatabase(); + + protected byte[] correctresponse; + protected byte[] wrongresponse; + + public void testSucessfulAuth() throws SaslException + { + byte[] resp = this.server.evaluateResponse(correctresponse); + assertNull(resp); + } + + public void testFailAuth() + { + boolean exceptionCaught = false; + try + { + byte[] resp = this.server.evaluateResponse(wrongresponse); + } + catch (SaslException e) + { + assertEquals("Authentication failed", e.getCause().getMessage()); + exceptionCaught = true; + } + if (!exceptionCaught) + { + fail("Should have thrown SaslException"); + } + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.java new file mode 100644 index 0000000000..a87c727a9a --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalDatabase.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.security.auth.sasl; + +import java.io.IOException; +import java.security.Principal; +import java.util.List; +import java.util.Map; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; + +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; + +public class TestPrincipalDatabase implements PrincipalDatabase +{ + + public boolean createPrincipal(Principal principal, char[] password) + { + // TODO Auto-generated method stub + return false; + } + + public boolean deletePrincipal(Principal principal) throws AccountNotFoundException + { + // TODO Auto-generated method stub + return false; + } + + public Map getMechanisms() + { + // TODO Auto-generated method stub + return null; + } + + public Principal getUser(String username) + { + // TODO Auto-generated method stub + return null; + } + + public List getUsers() + { + // TODO Auto-generated method stub + return null; + } + + public void setPassword(Principal principal, PasswordCallback callback) throws IOException, + AccountNotFoundException + { + callback.setPassword("p".toCharArray()); + } + + public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException + { + // TODO Auto-generated method stub + return false; + } + + public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException + { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java new file mode 100644 index 0000000000..6245064bf7 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/amqplain/AMQPlainSaslServerTest.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.security.auth.sasl.amqplain; + +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.server.security.auth.sasl.SaslServerTestCase; +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; + +public class AMQPlainSaslServerTest extends SaslServerTestCase +{ + protected void setUp() throws Exception + { + UsernamePasswordInitialiser handler = new AmqPlainInitialiser(); + handler.initialise(db); + this.server = new AmqPlainSaslServer(handler.getCallbackHandler()); + FieldTable table = FieldTableFactory.newFieldTable(); + table.setString("LOGIN", username); + table.setString("PASSWORD", password); + correctresponse = table.getDataAsBytes(); + table.setString("PASSWORD", notpassword); + wrongresponse = table.getDataAsBytes(); + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.java new file mode 100644 index 0000000000..5dd51250dc --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerTest.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.server.security.auth.sasl.plain; + +import org.apache.qpid.server.security.auth.sasl.SaslServerTestCase; +import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; + +public class PlainSaslServerTest extends SaslServerTestCase +{ + + protected void setUp() throws Exception + { + UsernamePasswordInitialiser handler = new PlainInitialiser(); + handler.initialise(db); + this.server = new PlainSaslServer(handler.getCallbackHandler()); + correctresponse = new byte[]{0x0, (byte) username.charAt(0), 0x0, (byte) password.charAt(0)}; + wrongresponse = new byte[]{0x0,(byte) username.charAt(0), 0x0, (byte) notpassword.charAt(0)}; + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java new file mode 100644 index 0000000000..a695a67eea --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreShutdownTest.java @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; + +import java.util.List; + +public class MessageStoreShutdownTest extends InternalBrokerBaseCase +{ + + public void test() + { + subscribe(_session, _channel, _queue); + + try + { + publishMessages(_session, _channel, 1); + } + catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + fail(e.getMessage()); + } + + try + { + _registry.close(); + } + catch (Exception e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + fail(e.getMessage()); + } + + assertTrue("Session should now be closed", _session.isClosed()); + + + //Test attempting to modify the broker state after session has been closed. + + //The Message should have been removed from the unacked list. + + //Ack Messages + List list = _session.getDelivers(_channel.getChannelId(), new AMQShortString("sgen_1"), 1); + + InternalTestProtocolSession.DeliveryPair pair = list.get(0); + + try + { + // The message should now be requeued and so unable to ack it. + _channel.acknowledgeMessage(pair.getDeliveryTag(), false); + } + catch (AMQException e) + { + assertEquals("Incorrect exception thrown", "Single ack on delivery tag 1 not known for channel:1", e.getMessage()); + } + + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java new file mode 100644 index 0000000000..dec4de4cc6 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -0,0 +1,644 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; +import org.apache.qpid.server.exchange.DirectExchange; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.exchange.TopicExchange; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.IncomingMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.AMQPriorityQueue; +import org.apache.qpid.server.queue.SimpleAMQQueue; +import org.apache.qpid.server.queue.ExchangeBinding; +import org.apache.qpid.server.txn.NonTransactionalContext; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.amqp_8_0.BasicConsumeBodyImpl; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.List; + +/** + * This tests the MessageStores by using the available interfaces. + * + * This test validates that Exchanges, Queues, Bindings and Messages are persisted correctly. + */ +public class MessageStoreTest extends TestCase +{ + + private static final int DEFAULT_PRIORTY_LEVEL = 5; + private static final Logger _logger = LoggerFactory.getLogger(MessageStoreTest.class); + + public void testMemoryMessageStore() + { + + PropertiesConfiguration config = new PropertiesConfiguration(); + + config.addProperty("store.class", "org.apache.qpid.server.store.MemoryMessageStore"); + + runTestWithStore(config); + } + + public void DISABLE_testDerbyMessageStore() + { + PropertiesConfiguration config = new PropertiesConfiguration(); + + config.addProperty("store.environment-path", "derbyDB_MST"); + config.addProperty("store.class", "org.apache.qpid.server.store.DerbyMessageStore"); + + runTestWithStore(config); + } + + private void reload(Configuration configuration) + { + if (_virtualHost != null) + { + try + { + _virtualHost.close(); + } + catch (Exception e) + { + fail(e.getMessage()); + } + } + + try + { + _virtualHost = new VirtualHost(virtualHostName, configuration, null); + ApplicationRegistry.getInstance().getVirtualHostRegistry().registerVirtualHost(_virtualHost); + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + VirtualHost _virtualHost = null; + String virtualHostName = "MessageStoreTest"; + + AMQShortString nonDurableExchangeName = new AMQShortString("MST-NonDurableDirectExchange"); + AMQShortString directExchangeName = new AMQShortString("MST-DirectExchange"); + AMQShortString topicExchangeName = new AMQShortString("MST-TopicExchange"); + AMQShortString queueOwner = new AMQShortString("MST"); + + AMQShortString durablePriorityTopicQueueName = new AMQShortString("MST-PriorityTopicQueue-Durable"); + AMQShortString durableTopicQueueName = new AMQShortString("MST-TopicQueue-Durable"); + AMQShortString priorityTopicQueueName = new AMQShortString("MST-PriorityTopicQueue"); + AMQShortString topicQueueName = new AMQShortString("MST-TopicQueue"); + + AMQShortString durablePriorityQueueName = new AMQShortString("MST-PriorityQueue-Durable"); + AMQShortString durableQueueName = new AMQShortString("MST-Queue-Durable"); + AMQShortString priorityQueueName = new AMQShortString("MST-PriorityQueue"); + AMQShortString queueName = new AMQShortString("MST-Queue"); + + AMQShortString directRouting = new AMQShortString("MST-direct"); + AMQShortString topicRouting = new AMQShortString("MST-topic"); + + protected void setUp() + { + ApplicationRegistry.getInstance(1); + } + + protected void tearDown() + { + ApplicationRegistry.remove(1); + } + + protected void runTestWithStore(Configuration configuration) + { + //Ensure Environment Path is empty + cleanup(configuration); + + //Load the Virtualhost with the required MessageStore + reload(configuration); + + MessageStore messageStore = _virtualHost.getMessageStore(); + + createAllQueues(); + createAllTopicQueues(); + + //Register Non-Durable DirectExchange + Exchange nonDurableExchange = createExchange(DirectExchange.TYPE, nonDurableExchangeName, false); + bindAllQueuesToExchange(nonDurableExchange, directRouting); + + //Register DirectExchange + Exchange directExchange = createExchange(DirectExchange.TYPE, directExchangeName, true); + bindAllQueuesToExchange(directExchange, directRouting); + + //Register TopicExchange + Exchange topicExchange = createExchange(TopicExchange.TYPE, topicExchangeName, true); + bindAllTopicQueuesToExchange(topicExchange, topicRouting); + + //Send Message To NonDurable direct Exchange = persistent + sendMessageOnExchange(nonDurableExchange, directRouting, true); + // and non-persistent + sendMessageOnExchange(nonDurableExchange, directRouting, false); + + //Send Message To direct Exchange = persistent + sendMessageOnExchange(directExchange, directRouting, true); + // and non-persistent + sendMessageOnExchange(directExchange, directRouting, false); + + //Send Message To topic Exchange = persistent + sendMessageOnExchange(topicExchange, topicRouting, true); + // and non-persistent + sendMessageOnExchange(topicExchange, topicRouting, false); + + //Ensure all the Queues have four messages (one transient, one persistent) x 2 exchange routings + validateMessageOnQueues(4, true); + //Ensure all the topics have two messages (one transient, one persistent) + validateMessageOnTopics(2, true); + + assertEquals("Not all queues correctly registered", 8, _virtualHost.getQueueRegistry().getQueues().size()); + + if (!messageStore.isPersistent()) + { + _logger.warn("Unable to test Persistent capabilities of messages store(" + messageStore.getClass() + ") as it is not capable of peristence."); + return; + } + + //Reload the Virtualhost to test persistence + _logger.info("Reloading Virtualhost"); + + VirtualHost original = _virtualHost; + + reload(configuration); + + assertTrue("Virtualhost has not been reloaded", original != _virtualHost); + + validateExchanges(); + + //Validate Durable Queues still have the persistentn message + validateMessageOnQueues(2, false); + //Validate Durable Queues still have the persistentn message + validateMessageOnTopics(1, false); + + //Validate Properties of Binding + validateBindingProperties(); + + //Validate Properties of Queues + validateQueueProperties(); + + //Validate Non-Durable Queues are gone. + assertNull("Non-Durable queue still registered:" + priorityQueueName, _virtualHost.getQueueRegistry().getQueue(priorityQueueName)); + assertNull("Non-Durable queue still registered:" + queueName, _virtualHost.getQueueRegistry().getQueue(queueName)); + assertNull("Non-Durable queue still registered:" + priorityTopicQueueName, _virtualHost.getQueueRegistry().getQueue(priorityTopicQueueName)); + assertNull("Non-Durable queue still registered:" + topicQueueName, _virtualHost.getQueueRegistry().getQueue(topicQueueName)); + + assertEquals("Not all queues correctly registered", 4, _virtualHost.getQueueRegistry().getQueues().size()); + } + + private void validateExchanges() + { + ExchangeRegistry registry = _virtualHost.getExchangeRegistry(); + + assertTrue(directExchangeName + " exchange NOT reloaded after failover", + registry.getExchangeNames().contains(directExchangeName)); + assertTrue(topicExchangeName + " exchange NOT reloaded after failover", + registry.getExchangeNames().contains(topicExchangeName)); + assertTrue(nonDurableExchangeName + " exchange reloaded after failover", + !registry.getExchangeNames().contains(nonDurableExchangeName)); + + // There are 5 required exchanges + our 2 durable queues + assertEquals("Incorrect number of exchanges available", 5 + 2, registry.getExchangeNames().size()); + } + + /** Validates that the Durable queues */ + private void validateBindingProperties() + { + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + validateBindingProperties(queueRegistry.getQueue(durablePriorityQueueName).getExchangeBindings(), false); + validateBindingProperties(queueRegistry.getQueue(durablePriorityTopicQueueName).getExchangeBindings(), true); + validateBindingProperties(queueRegistry.getQueue(durableQueueName).getExchangeBindings(), false); + validateBindingProperties(queueRegistry.getQueue(durableTopicQueueName).getExchangeBindings(), true); + } + + /** + * Validate that each queue is bound once. + * + * @param bindings the set of bindings to validate + * @param useSelectors if set validate that the binding has a JMS_SELECTOR argument + */ + private void validateBindingProperties(List bindings, boolean useSelectors) + { + assertEquals("Each queue should only be bound once.", 1, bindings.size()); + + ExchangeBinding binding = bindings.get(0); + + if (useSelectors) + { + assertTrue("Binding does not contain a Selector argument.", + binding.getArguments().containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue())); + } + } + + private void validateQueueProperties() + { + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + validateQueueProperties(queueRegistry.getQueue(durablePriorityQueueName), true); + validateQueueProperties(queueRegistry.getQueue(durablePriorityTopicQueueName), true); + validateQueueProperties(queueRegistry.getQueue(durableQueueName), false); + validateQueueProperties(queueRegistry.getQueue(durableTopicQueueName), false); + + } + + private void validateQueueProperties(AMQQueue queue, boolean usePriority) + { + if (usePriority) + { + assertEquals("Queue is no longer a Priority Queue", AMQPriorityQueue.class, queue.getClass()); + assertEquals("Priority Queue does not have set priorities", DEFAULT_PRIORTY_LEVEL, ((AMQPriorityQueue) queue).getPriorities()); + } + else + { + assertEquals("Queue is no longer a Priority Queue", SimpleAMQQueue.class, queue.getClass()); + } + } + + /** + * Delete the Store Environment path + * + * @param configuration The configuration that contains the store environment path. + */ + private void cleanup(Configuration configuration) + { + + String environment = configuration.getString("store.environment-path"); + + if (environment != null) + { + File environmentPath = new File(environment); + + if (environmentPath.exists()) + { + deleteDirectory(environmentPath); + } + } + } + + private void deleteDirectory(File path) + { + if (path.isDirectory()) + { + for (File file : path.listFiles()) + { + deleteDirectory(file); + } + } + else + { + path.delete(); + } + } + + private void sendMessageOnExchange(Exchange directExchange, AMQShortString routingKey, boolean deliveryMode) + { + //Set MessagePersustebce + BasicContentHeaderProperties properties = new BasicContentHeaderProperties(); + properties.setDeliveryMode(deliveryMode ? Integer.valueOf(2).byteValue() : Integer.valueOf(1).byteValue()); + FieldTable headers = properties.getHeaders(); + headers.setString("Test", "MST"); + properties.setHeaders(headers); + + MessagePublishInfo messageInfo = new TestMessagePublishInfo(directExchange, false, false, routingKey); + + IncomingMessage currentMessage = null; + + try + { + currentMessage = new IncomingMessage(_virtualHost.getMessageStore().getNewMessageId(), + messageInfo, + new NonTransactionalContext(_virtualHost.getMessageStore(), + new StoreContext(), null, null), + new InternalTestProtocolSession()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + currentMessage.setMessageStore(_virtualHost.getMessageStore()); + currentMessage.setExchange(directExchange); + + ContentHeaderBody headerBody = new ContentHeaderBody(); + headerBody.classId = BasicConsumeBodyImpl.CLASS_ID; + headerBody.bodySize = 0; + + headerBody.properties = properties; + + try + { + currentMessage.setContentHeaderBody(headerBody); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + currentMessage.setExpiration(); + + try + { + currentMessage.route(); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + currentMessage.routingComplete(_virtualHost.getMessageStore(), new MessageHandleFactory()); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + // check and deliver if header says body length is zero + if (currentMessage.allContentReceived()) + { + try + { + currentMessage.deliverToQueues(); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + } + + private void createAllQueues() + { + //Register Durable Priority Queue + createQueue(durablePriorityQueueName, true, true); + + //Register Durable Simple Queue + createQueue(durableQueueName, false, true); + + //Register NON-Durable Priority Queue + createQueue(priorityQueueName, true, false); + + //Register NON-Durable Simple Queue + createQueue(queueName, false, false); + } + + private void createAllTopicQueues() + { + //Register Durable Priority Queue + createQueue(durablePriorityTopicQueueName, true, true); + + //Register Durable Simple Queue + createQueue(durableTopicQueueName, false, true); + + //Register NON-Durable Priority Queue + createQueue(priorityTopicQueueName, true, false); + + //Register NON-Durable Simple Queue + createQueue(topicQueueName, false, false); + } + + private Exchange createExchange(ExchangeType type, AMQShortString name, boolean durable) + { + Exchange exchange = null; + + try + { + exchange = type.newInstance(_virtualHost, name, durable, 0, false); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + _virtualHost.getExchangeRegistry().registerExchange(exchange); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + return exchange; + } + + private void createQueue(AMQShortString queueName, boolean usePriority, boolean durable) + { + + FieldTable queueArguments = null; + + if (usePriority) + { + queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + } + + AMQQueue queue = null; + + //Ideally we would be able to use the QueueDeclareHandler here. + try + { + queue = AMQQueueFactory.createAMQQueueImpl(queueName, durable, queueOwner, false, _virtualHost, + queueArguments); + + validateQueueProperties(queue, usePriority); + + if (queue.isDurable() && !queue.isAutoDelete()) + { + _virtualHost.getMessageStore().createQueue(queue, queueArguments); + } + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + try + { + _virtualHost.getQueueRegistry().registerQueue(queue); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + } + + private void bindAllQueuesToExchange(Exchange exchange, AMQShortString routingKey) + { + FieldTable queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durablePriorityQueueName), false, queueArguments); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durableQueueName), false, null); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(priorityQueueName), false, queueArguments); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(queueName), false, null); + } + + private void bindAllTopicQueuesToExchange(Exchange exchange, AMQShortString routingKey) + { + FieldTable queueArguments = new FieldTable(); + queueArguments.put(AMQQueueFactory.X_QPID_PRIORITIES, DEFAULT_PRIORTY_LEVEL); + + QueueRegistry queueRegistry = _virtualHost.getQueueRegistry(); + + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durablePriorityTopicQueueName), true, queueArguments); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(durableTopicQueueName), true, null); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(priorityTopicQueueName), true, queueArguments); + bindQueueToExchange(exchange, routingKey, queueRegistry.getQueue(topicQueueName), true, null); + } + + + protected void bindQueueToExchange(Exchange exchange, AMQShortString routingKey, AMQQueue queue, boolean useSelector, FieldTable queueArguments) + { + try + { + exchange.registerQueue(queueName, queue, queueArguments); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + FieldTable bindArguments = null; + + if (useSelector) + { + bindArguments = new FieldTable(); + bindArguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), "Test = 'MST'"); + } + + try + { + queue.bind(exchange, routingKey, bindArguments); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + } + + private void validateMessage(long messageCount, boolean allQueues) + { + validateMessageOnTopics(messageCount, allQueues); + validateMessageOnQueues(messageCount, allQueues); + } + + private void validateMessageOnTopics(long messageCount, boolean allQueues) + { + validateMessageOnQueue(durablePriorityTopicQueueName, messageCount); + validateMessageOnQueue(durableTopicQueueName, messageCount); + + if (allQueues) + { + validateMessageOnQueue(priorityTopicQueueName, messageCount); + validateMessageOnQueue(topicQueueName, messageCount); + } + } + + private void validateMessageOnQueues(long messageCount, boolean allQueues) + { + validateMessageOnQueue(durablePriorityQueueName, messageCount); + validateMessageOnQueue(durableQueueName, messageCount); + + if (allQueues) + { + validateMessageOnQueue(priorityQueueName, messageCount); + validateMessageOnQueue(queueName, messageCount); + } + } + + private void validateMessageOnQueue(AMQShortString queueName, long messageCount) + { + AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueName); + + assertNotNull("Queue(" + queueName + ") not correctly registered:", queue); + + assertEquals("Incorrect Message count on queue:" + queueName, messageCount, queue.getMessageCount()); + } + + private class TestMessagePublishInfo implements MessagePublishInfo + { + + Exchange _exchange; + boolean _immediate; + boolean _mandatory; + AMQShortString _routingKey; + + TestMessagePublishInfo(Exchange exchange, boolean immediate, boolean mandatory, AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange.getName(); + } + + public void setExchange(AMQShortString exchange) + { + //no-op + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } +} \ No newline at end of file diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java new file mode 100644 index 0000000000..9146fe88ae --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java @@ -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. + * + */ +package org.apache.qpid.server.store; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.abstraction.ContentChunk; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.HashMap; +import java.util.List; + +/** + * Adds some extra methods to the memory message store for testing purposes. + */ +public class TestableMemoryMessageStore extends MemoryMessageStore +{ + + MemoryMessageStore _mms = null; + private HashMap _messages = new HashMap(); + + public TestableMemoryMessageStore(MemoryMessageStore mms) + { + _mms = mms; + } + + public TestableMemoryMessageStore() + { + _metaDataMap = new ConcurrentHashMap(); + _contentBodyMap = new ConcurrentHashMap>(); + } + + public ConcurrentMap getMessageMetaDataMap() + { + if (_mms != null) + { + return _mms._metaDataMap; + } + else + { + return _metaDataMap; + } + } + + public ConcurrentMap> getContentBodyMap() + { + if (_mms != null) + { + return _mms._contentBodyMap; + } + else + { + return _contentBodyMap; + } + } + + public void enqueueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + getMessages().put(messageId, queue); + } + + public void dequeueMessage(StoreContext context, final AMQQueue queue, Long messageId) throws AMQException + { + getMessages().remove(messageId); + } + + public HashMap getMessages() + { + return _messages; + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java new file mode 100644 index 0000000000..33fd669d5c --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -0,0 +1,180 @@ +package org.apache.qpid.server.subscription; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ + +import java.util.ArrayList; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.queue.QueueEntry.SubscriptionAcquiredState; + +public class MockSubscription implements Subscription +{ + + private boolean _closed = false; + private AMQShortString tag = new AMQShortString("mocktag"); + private AMQQueue queue = null; + private StateListener _listener = null; + private QueueEntry lastSeen = null; + private State _state = State.ACTIVE; + private ArrayList messages = new ArrayList(); + private final Lock _stateChangeLock = new ReentrantLock(); + + public void close() + { + _closed = true; + if (_listener != null) + { + _listener.stateChange(this, _state, State.CLOSED); + } + _state = State.CLOSED; + } + + public boolean filtersMessages() + { + return false; + } + + public AMQChannel getChannel() + { + return null; + } + + public AMQShortString getConsumerTag() + { + return tag ; + } + + public QueueEntry getLastSeenEntry() + { + return lastSeen; + } + + public SubscriptionAcquiredState getOwningState() + { + return new QueueEntry.SubscriptionAcquiredState(this); + } + + public AMQQueue getQueue() + { + return queue; + } + + public void getSendLock() + { + _stateChangeLock.lock(); + } + + public boolean hasInterest(QueueEntry msg) + { + return true; + } + + public boolean isActive() + { + return true; + } + + public boolean isAutoClose() + { + return false; + } + + public boolean isBrowser() + { + return false; + } + + public boolean isClosed() + { + return _closed; + } + + public boolean isSuspended() + { + return false; + } + + public void queueDeleted(AMQQueue queue) + { + } + + public void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public void resend(QueueEntry entry) throws AMQException + { + } + + public void restoreCredit(QueueEntry queueEntry) + { + } + + public void send(QueueEntry msg) throws AMQException + { + lastSeen = msg; + messages.add(msg); + } + + public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue) + { + boolean result = false; + if (expected != null) + { + result = (expected.equals(lastSeen)); + } + lastSeen = newValue; + return result; + } + + public void setQueue(AMQQueue queue) + { + this.queue = queue; + } + + public void setStateListener(StateListener listener) + { + this._listener = listener; + } + + public State getState() + { + return _state; + } + + public boolean wouldSuspend(QueueEntry msg) + { + return false; + } + + public ArrayList getMessages() + { + return messages; + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.java new file mode 100644 index 0000000000..d0db4ebd38 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/subscription/QueueBrowserUsesNoAckTest.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.subscription; + +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.AMQException; + +import java.util.List; + +public class QueueBrowserUsesNoAckTest extends InternalBrokerBaseCase +{ + + public void testQueueBrowserUsesNoAck() throws AMQException + { + int sendMessageCount = 2; + int prefetch = 1; + + //Check store is empty + checkStoreContents(0); + + //Send required messsages to the queue + publishMessages(_session, _channel, sendMessageCount); + + //Ensure they are stored + checkStoreContents(sendMessageCount); + + //Check that there are no unacked messages + assertEquals("Channel should have no unacked msgs ", 0, + _channel.getUnacknowledgedMessageMap().size()); + + //Set the prefetch on the session to be less than the sent messages + _channel.setCredit(0, prefetch); + + //browse the queue + AMQShortString browser = browse(_channel, _queue); + + _queue.deliverAsync(); + + //Wait for messages to fill the prefetch + _session.awaitDelivery(prefetch); + + //Get those messages + List messages = + _session.getDelivers(_channel.getChannelId(), browser, + prefetch); + + //Ensure we recevied the prefetched messages + assertEquals(prefetch, messages.size()); + + //Check the process didn't suspend the subscription as this would + // indicate we are using the prefetch credit. i.e. using acks not No-Ack + assertTrue("The subscription has been suspended", + !_channel.getSubscription(browser).getState() + .equals(Subscription.State.SUSPENDED)); + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java new file mode 100644 index 0000000000..509ea817fd --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java @@ -0,0 +1,209 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.util; + +import junit.framework.TestCase; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.ConsumerTagNotUniqueException; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.AMQException; +import org.apache.qpid.util.MockChannel; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.exchange.ExchangeDefaults; + +public class InternalBrokerBaseCase extends TestCase +{ + protected IApplicationRegistry _registry; + protected MessageStore _messageStore; + protected MockChannel _channel; + protected InternalTestProtocolSession _session; + protected VirtualHost _virtualHost; + protected StoreContext _storeContext = new StoreContext(); + protected AMQQueue _queue; + protected AMQShortString QUEUE_NAME; + + public void setUp() throws Exception + { + super.setUp(); + _registry = new TestApplicationRegistry(); + ApplicationRegistry.initialise(_registry); + _virtualHost = _registry.getVirtualHostRegistry().getVirtualHost("test"); + + _messageStore = _virtualHost.getMessageStore(); + + QUEUE_NAME = new AMQShortString("test"); + _queue = AMQQueueFactory.createAMQQueueImpl(QUEUE_NAME, false, new AMQShortString("testowner"), + false, _virtualHost, null); + + _virtualHost.getQueueRegistry().registerQueue(_queue); + + Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + + _queue.bind(defaultExchange, QUEUE_NAME, null); + + _session = new InternalTestProtocolSession(); + + _session.setVirtualHost(_virtualHost); + + _channel = new MockChannel(_session, 1, _messageStore); + + _session.addChannel(_channel); + } + + public void tearDown() throws Exception + { + ApplicationRegistry.remove(1); + super.tearDown(); + } + + protected void checkStoreContents(int messageCount) + { + assertEquals("Message header count incorrect in the MetaDataMap", messageCount, ((TestableMemoryMessageStore) _messageStore).getMessageMetaDataMap().size()); + + //The above publish message is sufficiently small not to fit in the header so no Body is required. + //assertEquals("Message body count incorrect in the ContentBodyMap", messageCount, ((TestableMemoryMessageStore) _messageStore).getContentBodyMap().size()); + } + + protected AMQShortString subscribe(InternalTestProtocolSession session, AMQChannel channel, AMQQueue queue) + { + try + { + return channel.subscribeToQueue(null, queue, true, null, false, true); + } + catch (AMQException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + catch (ConsumerTagNotUniqueException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + //Keep the compiler happy + return null; + } + + protected AMQShortString browse(AMQChannel channel, AMQQueue queue) + { + try + { + FieldTable filters = new FieldTable(); + filters.put(AMQPFilterTypes.NO_CONSUME.getValue(), true); + + return channel.subscribeToQueue(null, queue, true, filters, false, true); + } + catch (AMQException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + catch (ConsumerTagNotUniqueException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + //Keep the compiler happy + return null; + } + + public void publishMessages(InternalTestProtocolSession session, AMQChannel channel, int messages) throws AMQException + { + MessagePublishInfo info = new MessagePublishInfo() + { + public AMQShortString getExchange() + { + return ExchangeDefaults.DEFAULT_EXCHANGE_NAME; + } + + public void setExchange(AMQShortString exchange) + { + + } + + public boolean isImmediate() + { + return false; + } + + public boolean isMandatory() + { + return false; + } + + public AMQShortString getRoutingKey() + { + return QUEUE_NAME; + } + }; + + for (int count = 0; count < messages; count++) + { + channel.setPublishFrame(info, _virtualHost.getExchangeRegistry().getExchange(info.getExchange())); + + //Set the body size + ContentHeaderBody _headerBody = new ContentHeaderBody(); + _headerBody.bodySize = 0; + + //Set Minimum properties + BasicContentHeaderProperties properties = new BasicContentHeaderProperties(); + + properties.setExpiration(0L); + properties.setTimestamp(System.currentTimeMillis()); + + //Make Message Persistent + properties.setDeliveryMode((byte) 2); + + _headerBody.properties = properties; + + channel.publishContentHeader(_headerBody); + } + + } + + public void acknowledge(AMQChannel channel, long deliveryTag) + { + try + { + channel.acknowledgeMessage(deliveryTag, false); + } + catch (AMQException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java new file mode 100644 index 0000000000..c7db51016e --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java @@ -0,0 +1,88 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import junit.framework.TestCase; + +public class LoggingProxyTest extends TestCase +{ + static interface IFoo { + void foo(); + void foo(int i, Collection c); + String bar(); + String bar(String s, List l); + } + + static class Foo implements IFoo { + public void foo() + { + } + + public void foo(int i, Collection c) + { + } + + public String bar() + { + return null; + } + + public String bar(String s, List l) + { + return "ha"; + } + } + + public void testSimple() { + LoggingProxy proxy = new LoggingProxy(new Foo(), 20); + IFoo foo = (IFoo)proxy.getProxy(IFoo.class); + foo.foo(); + assertEquals(2, proxy.getBufferSize()); + assertTrue(proxy.getBuffer().get(0).toString().matches(".*: foo\\(\\) entered$")); + assertTrue(proxy.getBuffer().get(1).toString().matches(".*: foo\\(\\) returned$")); + + foo.foo(3, Arrays.asList(0, 1, 2)); + assertEquals(4, proxy.getBufferSize()); + assertTrue(proxy.getBuffer().get(2).toString().matches(".*: foo\\(\\[3, \\[0, 1, 2\\]\\]\\) entered$")); + assertTrue(proxy.getBuffer().get(3).toString().matches(".*: foo\\(\\) returned$")); + + foo.bar(); + assertEquals(6, proxy.getBufferSize()); + assertTrue(proxy.getBuffer().get(4).toString().matches(".*: bar\\(\\) entered$")); + assertTrue(proxy.getBuffer().get(5).toString().matches(".*: bar\\(\\) returned null$")); + + foo.bar("hello", Arrays.asList(1, 2, 3)); + assertEquals(8, proxy.getBufferSize()); + assertTrue(proxy.getBuffer().get(6).toString().matches(".*: bar\\(\\[hello, \\[1, 2, 3\\]\\]\\) entered$")); + assertTrue(proxy.getBuffer().get(7).toString().matches(".*: bar\\(\\) returned ha$")); + + proxy.dump(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(LoggingProxyTest.class); + } +} diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java new file mode 100644 index 0000000000..15449dc613 --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -0,0 +1,123 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.util; + +import org.apache.commons.configuration.MapConfiguration; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.ACLPlugin; +import org.apache.qpid.server.security.access.plugins.AllowAll; +import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Properties; +import java.util.Arrays; + +public class TestApplicationRegistry extends ApplicationRegistry +{ + private QueueRegistry _queueRegistry; + + private ExchangeRegistry _exchangeRegistry; + + private ExchangeFactory _exchangeFactory; + + private MessageStore _messageStore; + + private VirtualHost _vHost; + + + public TestApplicationRegistry() + { + super(new MapConfiguration(new HashMap())); + } + + public void initialise() throws Exception + { + Properties users = new Properties(); + + users.put("guest", "guest"); + + _databaseManager = new PropertiesPrincipalDatabaseManager("default", users); + + _accessManager = new AllowAll(); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); + + _managedObjectRegistry = new NoopManagedObjectRegistry(); + + _messageStore = new TestableMemoryMessageStore(); + + _virtualHostRegistry = new VirtualHostRegistry(); + + _vHost = new VirtualHost("test", _messageStore); + + _virtualHostRegistry.registerVirtualHost(_vHost); + + _queueRegistry = _vHost.getQueueRegistry(); + _exchangeFactory = _vHost.getExchangeFactory(); + _exchangeRegistry = _vHost.getExchangeRegistry(); + + _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes + } + + public QueueRegistry getQueueRegistry() + { + return _queueRegistry; + } + + public ExchangeRegistry getExchangeRegistry() + { + return _exchangeRegistry; + } + + public ExchangeFactory getExchangeFactory() + { + return _exchangeFactory; + } + + public Collection getVirtualHostNames() + { + String[] hosts = {"test"}; + return Arrays.asList(hosts); + } + + public void setAccessManager(ACLPlugin newManager) + { + _accessManager = newManager; + } + + public MessageStore getMessageStore() + { + return _messageStore; + } + +} + + diff --git a/RC6/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java new file mode 100644 index 0000000000..447d09429d --- /dev/null +++ b/RC6/qpid/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.util; + +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; + +public class MockChannel extends AMQChannel +{ + public MockChannel(AMQProtocolSession session, int channelId, MessageStore messageStore) + throws AMQException + { + super(session, channelId, messageStore); + } + + public Subscription getSubscription(AMQShortString subscription) + { + return _tag2SubscriptionMap.get(subscription); + } + +} diff --git a/RC6/qpid/java/build.deps b/RC6/qpid/java/build.deps new file mode 100644 index 0000000000..f9c4004aec --- /dev/null +++ b/RC6/qpid/java/build.deps @@ -0,0 +1,104 @@ +backport-util-concurrent=lib/backport-util-concurrent-2.2.jar + +commons-cli=lib/commons-cli-1.0.jar +commons-codec=lib/commons-codec-1.3.jar +commons-collections=lib/commons-collections-3.2.jar +commons-configuration=lib/commons-configuration-1.2.jar +commons-lang=lib/commons-lang-2.2.jar +commons-logging=lib/commons-logging-1.0.4.jar +commons-pool=lib/commons-pool-1.4.jar + +geronimo-jms=lib/geronimo-jms_1.1_spec-1.0.jar + +junit=lib/junit-3.8.1.jar +junit4=lib/junit-4.4.jar + +jline=lib/jline-0.9.94.jar + +log4j=lib/log4j-1.2.12.jar + +mina-core=lib/mina-core-1.0.1.jar +mina-filter-ssl=lib/mina-filter-ssl-1.0.1.jar + +slf4j-api=lib/slf4j-api-1.4.0.jar +slf4j-log4j=lib/slf4j-log4j12-1.4.0.jar + +xalan=lib/xalan-2.7.0.jar + +osgi-core=lib/org.osgi.core-1.0.0.jar +felix-framework=lib/org.apache.felix.framework-1.0.0.jar + +geronimo-servlet=lib/geronimo-servlet_2.5_spec-1.2.jar +felix.libs=${osgi-core} ${felix-framework} + +common.libs=${slf4j-api} ${backport-util-concurrent} ${mina-core} \ + ${mina-filter-ssl} ${commons-codec} ${commons-lang} ${commons-collections} \ + ${commons-configuration} +client.libs=${common.libs} ${geronimo-jms} +tools.libs=${client.libs} +broker.libs=${common.libs} ${commons-cli} ${commons-logging} ${log4j} \ + ${slf4j-log4j} ${xalan} ${felix.libs} ${build.lib}/${project.name}-common-${project.version}.jar + +broker-plugins.libs=${common.libs} ${felix.libs} +management-client.libs=${client.libs} ${commons-pool} ${log4j} ${slf4j-log4j} ${geronimo-servlet} + +junit-toolkit.libs=${log4j} ${junit} ${slf4j-api} +test.libs=${slf4j-log4j} ${junit-toolkit.libs} +systests.libs=${client.libs} ${test.libs} ${broker.libs} +perftests.libs=${systests.libs} +integrationtests.libs=${systests.libs} + +client-example.libs=${client.libs} +testkit.libs=${client.libs} + +ibm-icu=lib/com.ibm.icu_3.4.4.jar +ecl-core-jface=lib/org.eclipse.jface_3.2.0.jar +ecl-core-commands=lib/org.eclipse.core.commands_3.2.0.jar +ecl-core-contenttype=lib/org.eclipse.core.contenttype_3.2.0.jar +ecl-core-expressions=lib/org.eclipse.core.expressions_3.2.0.jar +ecl-core-jobs=lib/org.eclipse.core.jobs_3.2.0.jar +ecl-core-runtime=lib/org.eclipse.core.runtime_3.2.0.jar +ecl-core-runtime-compat-auth=lib/org.eclipse.core.runtime.compatibility.auth_3.2.0.jar +ecl-core-runtime-compat-registry=lib/org.eclipse.core.runtime.compatibility.registry_3.2.0.jar +ecl-equinox-common=lib/org.eclipse.equinox.common_3.2.0.jar +ecl-equinox-prefs=lib/org.eclipse.equinox.preferences_3.2.0.jar +ecl-equinox-registry=lib/org.eclipse.equinox.registry_3.2.0.jar +ecl-help=lib/org.eclipse.help_3.2.0.jar +ecl-osgi=lib/org.eclipse.osgi_3.4.2.R34x_v20080826-1230.jar +ecl-swt=lib/org.eclipse.swt_3.4.1.v3449c.jar +ecl-swt-win32-win32-x86=lib/org.eclipse.swt.win32.win32.x86_3.2.0.jar +ecl-unix-shared=lib/org.eclipse.equinox.launcher_1.0.101.R34x_v20080819.jar +ecl-swt-linux-gtk-x86=lib/org.eclipse.swt.gtk.linux.x86_3.4.1.v3449c.jar \ + lib/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.101.R34x_v20080805/* +ecl-swt-macosx=lib/org.eclipse.swt.carbon.macosx_3.4.1.v3449c.jar \ + lib/org.eclipse.equinox.launcher.carbon.macosx_1.0.101.R34x_v20080731/* + +ecl-ui=lib/org.eclipse.ui_3.2.0.jar +ecl-ui-forms=lib/org.eclipse.ui.forms_3.2.0.jar +ecl-ui-workbench=lib/org.eclipse.ui.workbench_3.2.1.jar + +management-eclipse-plugin.core-libs=${ibm-icu} ${ecl-core-jface} \ + ${ecl-core-commands} ${ecl-core-contenttype} ${ecl-core-expressions} \ + ${ecl-core-jobs} ${ecl-core-runtime} ${ecl-core-runtime-compat-auth} \ + ${ecl-core-runtime-compat-registry} ${ecl-equinox-common} \ + ${ecl-equinox-prefs} ${ecl-equinox-registry} ${ecl-help} ${ecl-osgi} \ + ${ecl-swt} ${ecl-ui} ${ecl-ui-forms} ${ecl-ui-workbench} + +management-eclipse-plugin-win32-win32-x86.libs=${management-eclipse-plugin.core-libs} ${ecl-swt-win32-win32-x86} +management-eclipse-plugin-linux-gtk-x86.libs=${management-eclipse-plugin.core-libs} ${ecl-unix-shared} ${ecl-swt-linux-gtk-x86} +management-eclipse-plugin-macosx.libs=${management-eclipse-plugin.core-libs} ${ecl-unix-shared} ${ecl-swt-macosx} + +management-eclipse-plugin.libs=${management-eclipse-plugin.core-libs} ${ecl-swt-win32-win32-x86} ${ecl-swt-linux-gtk-x86} ${ecl-swt-macosx} + +management-tools-qpid-cli.libs=${common.libs} ${jline} + +common.test.libs=${test.libs} +broker.test.libs=${test.libs} +client.test.libs=${broker.libs} ${test.libs} +client-example.test.libs=${test.libs} +tools.test.libs=${client.test.libs} +testkit.test.libs=${test.libs} +management-client.test.libs=${client.test.libs} +management-eclipse-plugin.test.libs=${systests.libs} +broker-plugins.test.libs=${test.libs} +management-tools-qpid-cli.test.libs=${junit4} ${slf4j-log4j} ${log4j} ${client.libs} diff --git a/RC6/qpid/java/build.xml b/RC6/qpid/java/build.xml new file mode 100644 index 0000000000..cea9b34b0e --- /dev/null +++ b/RC6/qpid/java/build.xml @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/clean-dir b/RC6/qpid/java/clean-dir new file mode 100755 index 0000000000..4d6141b4ab --- /dev/null +++ b/RC6/qpid/java/clean-dir @@ -0,0 +1,25 @@ + +#!/bin/bash +# +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# + + +rm -rf $@; mkdir $@ diff --git a/RC6/qpid/java/client-java14/README.txt b/RC6/qpid/java/client-java14/README.txt new file mode 100644 index 0000000000..66621c7eb2 --- /dev/null +++ b/RC6/qpid/java/client-java14/README.txt @@ -0,0 +1,33 @@ +An implementation of SASL is provided here, for the PLAIN and CRAM-MD5 mechanisms as well as maven assembly +instructions for producing a backported Java client for Java 1.4. In order to use the custom SASL implementation +on JRE 1.4 the following steps must be taken: + + * Install the SASL JSR-28 API jar. + * Install the qpid-client-java14 jar or set it up as a dynamically registered SASL provider. + * Set up java.security to add the SASL provider to the list of security providers if hte SASL implemenation jar was installed as an extension. + +Installing the SASL JSR-28 API jar. + + Download, http://www.worldspot.com/jsr28/v1.1/download/sasl.jar, and copy it to the JAVA_HOME\lib\ext directory. + +Install or set up the qpid-client-java14 jar. + + Copy the output jar for this project, qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar, to JAVA_HOME\lib\ext. + + OR + + Create a properties file and register the SASL implementations in the jar so that Qpids dynamic SASL registry can find them. In a properties file + add the lines: + + PLAIN=org.apache.qpid.sasl.ClientFactoryImpl + CRAM-MD5=org.apache.qpid.sasl.ClientFactoryImpl + + Place this somewhere on the classpath and put the qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar on the classpath too. When starting your application + pass in the system property amq.dynamicsaslregistrar.properties and set it to point to the location of the properties file. + +Set up the SASL provider. + + You only need to do this if the custom SASL jar, qpid-client-java14-1.0-incubating-M2-SNAPSHOT.jar, was copied to JAVA_HOME\lib\ext. + Add the following line to JAVA_HOME\lib\security\java.security file (where n is the providers preference order): + + security.provider.n=org.apache.qpid.sasl.Provider \ No newline at end of file diff --git a/RC6/qpid/java/client-java14/etc/sasl.properties b/RC6/qpid/java/client-java14/etc/sasl.properties new file mode 100644 index 0000000000..04519e2a30 --- /dev/null +++ b/RC6/qpid/java/client-java14/etc/sasl.properties @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +PLAIN=org.apache.qpid.sasl.ClientFactoryImpl +CRAM-MD5=org.apache.qpid.sasl.ClientFactoryImpl diff --git a/RC6/qpid/java/client-java14/src/main/assembly/client-java14-bin.xml b/RC6/qpid/java/client-java14/src/main/assembly/client-java14-bin.xml new file mode 100644 index 0000000000..91e1f23975 --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/assembly/client-java14-bin.xml @@ -0,0 +1,74 @@ + + + + + java-client-java14-bin + false + + tar.gz + zip + + + + + + ../resources + qpid-${qpid.version} + + DISCLAIMER + LICENSE.txt + NOTICE.txt + README.txt + + + + + ../release-docs + qpid-${qpid.version}/docs + + RELEASE_NOTES.txt + + + + + + + + qpid-${qpid.version}/lib + false + + + + org.apache.qpid:qpid-client:jar + org.apache.qpid:qpid-common:jar + + + org.apache.qpid:qpid-integrationtests:jar:java14 + + + org.apache.mina:mina-java5 + org.apache.mina:mina-filter-ssl + + + + + + + diff --git a/RC6/qpid/java/client-java14/src/main/assembly/jar-with-dependencies.xml b/RC6/qpid/java/client-java14/src/main/assembly/jar-with-dependencies.xml new file mode 100644 index 0000000000..091fd46427 --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/assembly/jar-with-dependencies.xml @@ -0,0 +1,104 @@ + + + + + + all-test-deps + false + + jar + + + + + + + + + true + + + + org.apache.qpid:qpid-client:jar + org.apache.qpid:qpid-common:jar + + + org.apache.mina:mina-java5 + org.apache.mina:mina-filter-ssl + + + + + \ No newline at end of file diff --git a/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java new file mode 100644 index 0000000000..ed8e4ad80f --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java @@ -0,0 +1,343 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.sasl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.security.auth.callback.*; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; + +import org.apache.log4j.Logger; + +import org.apache.qpid.util.PrettyPrintingUtils; + +/** + * Implements a factory for generating Sasl client implementations. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Provide a list of supported encryption mechansims that meet a defined set of Sasl properties. + *
Provide the best matching supported Sasl mechanism to a preference ordered list of mechanisms and Sasl + * properties. + *
Perform username and password request call backs. CallBackHandler + *
+ */ +public class ClientFactoryImpl implements SaslClientFactory +{ + //private static final Logger log = Logger.getLogger(ClientFactoryImpl.class); + + /** Holds the names of the supported encryption mechanisms. */ + private static final String[] SUPPORTED_MECHANISMS = { "CRAM-MD5", "PLAIN" }; + + /** Defines index of the CRAM-MD5 mechanism within the supported mechanisms. */ + private static final int CRAM_MD5 = 0; + + /** Defines index of the PLAIN mechanism within the supported mechanisms. */ + private static final int PLAIN = 1; + + /** Bit mapping of the no plain text policy. */ + private static final int NOPLAINTEXT = 0x0001; + + /** Bit mapping of the no susceptible active attacks policy. */ + private static final int NOACTIVE = 0x0002; + + /** Bit mapping of the no susceptible to dictionary attacks policy. */ + private static final int NODICTIONARY = 0x0004; + + /** Bit mapping of the must use forward secrecy between sessions policy. */ + private static final int FORWARD_SECRECY = 0x0008; + + /** Bit mapping of the no anonymous logins policy. */ + private static final int NOANONYMOUS = 0x0010; + + /** Bit mapping of the must pass credentials policy. */ + private static final int PASS_CREDENTIALS = 0x0020; + + /** Defines a mapping from supported mechanisms to supported policy flags. */ + private static final int[] SUPPPORTED_MECHANISMS_POLICIES = + { + NOPLAINTEXT | NOANONYMOUS, // CRAM-MD5 + NOANONYMOUS // PLAIN + }; + + /** + * Creates a SaslClient using the parameters supplied. + * + * @param mechanisms The non-null list of mechanism names to try. Each is the IANA-registered name of a SASL + * mechanism. (e.g. "GSSAPI", "CRAM-MD5"). + * @param authorizationId The possibly null protocol-dependent identification to be used for authorization. + * If null or empty, the server derives an authorization ID from the client's authentication + * credentials. When the SASL authentication completes successfully, the specified entity is + * granted access. + * @param protocol The non-null string name of the protocol for which the authentication is being performed + * (e.g., "ldap"). + * @param serverName The non-null fully qualified host name of the server to authenticate to. + * @param props The possibly null set of properties used to select the SASL mechanism and to configure the + * authentication exchange of the selected mechanism. See the Sasl class for a list + * of standard properties. Other, possibly mechanism-specific, properties can be included. + * Properties not relevant to the selected mechanism are ignored. + * @param cbh The possibly null callback handler to used by the SASL mechanisms to get further + * information from the application/library to complete the authentication. For example, a + * SASL mechanism might require the authentication ID, password and realm from the caller. + * The authentication ID is requested by using a NameCallback. + * The password is requested by using a PasswordCallback. + * The realm is requested by using a RealmChoiceCallback if there is a list + * of realms to choose from, and by using a RealmCallback if + * the realm must be entered. + * + * @return A possibly null SaslClient created using the parameters supplied. If null, this factory cannot + * produce a SaslClient using the parameters supplied. + * + * @throws javax.security.sasl.SaslException If cannot create a SaslClient because of an error. + */ + public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, + Map props, CallbackHandler cbh) throws SaslException + { + /*log.debug("public SaslClient createSaslClient(String[] mechanisms = " + PrettyPrintingUtils.printArray(mechanisms) + + ", String authorizationId = " + authorizationId + ", String protocol = " + protocol + + ", String serverName = " + serverName + ", Map props = " + props + ", CallbackHandler cbh): called");*/ + + // Get a list of all supported mechanisms that matched the required properties. + String[] matchingMechanisms = getMechanismNames(props); + //log.debug("matchingMechanisms = " + PrettyPrintingUtils.printArray(matchingMechanisms)); + + // Scan down the list of mechanisms until the first one that matches one of the matching supported mechanisms + // is found. + String chosenMechanism = null; + + for (int i = 0; i < mechanisms.length; i++) + { + String mechanism = mechanisms[i]; + + for (int j = 0; j < matchingMechanisms.length; j++) + { + String matchingMechanism = matchingMechanisms[j]; + + if (mechanism.equals(matchingMechanism)) + { + chosenMechanism = mechanism; + + break; + } + } + + // Stop scanning if a match has been found. + if (chosenMechanism != null) + { + break; + } + } + + // Check that a matching mechanism was found or return null otherwise. + if (chosenMechanism == null) + { + //log.debug("No matching mechanism could be found."); + + return null; + } + + // Instantiate an appropriate client type for the chosen mechanism. + if (chosenMechanism.equals(SUPPORTED_MECHANISMS[CRAM_MD5])) + { + Object[] uinfo = getUserInfo("CRAM-MD5", authorizationId, cbh); + + //log.debug("Using CRAM-MD5 mechanism."); + + return new CramMD5Client((String) uinfo[0], (byte[]) uinfo[1]); + } + else + { + Object[] uinfo = getUserInfo("PLAIN", authorizationId, cbh); + + //log.debug("Using PLAIN mechanism."); + + return new PlainClient(authorizationId, (String) uinfo[0], (byte[]) uinfo[1]); + } + } + + /** + * Returns an array of names of mechanisms that match the specified + * mechanism selection policies. + * + * @param props The possibly null set of properties used to specify the + * security policy of the SASL mechanisms. For example, if props + * contains the Sasl.POLICY_NOPLAINTEXT property with the value + * "true", then the factory must not return any SASL mechanisms + * that are susceptible to simple plain passive attacks. + * See the Sasl class for a complete list of policy properties. + * Non-policy related properties, if present in props, are ignored. + * + * @return A non-null array containing a IANA-registered SASL mechanism names. + */ + public String[] getMechanismNames(Map props) + { + //log.debug("public String[] getMechanismNames(Map props = " + props + "): called"); + + // Used to build up the valid mechanisms in. + List validMechanisms = new ArrayList(); + + // Transform the Sasl properties into a set of bit mapped flags indicating the required properties of the + // encryption mechanism employed. + int requiredFlags = bitMapSaslProperties(props); + //log.debug("requiredFlags = " + requiredFlags); + + // Scan down the list of supported mechanisms filtering in only those that satisfy all of the desired + // encryption properties. + for (int i = 0; i < SUPPORTED_MECHANISMS.length; i++) + { + int mechanismFlags = SUPPPORTED_MECHANISMS_POLICIES[i]; + //log.debug("mechanismFlags = " + mechanismFlags); + + // Check if the current mechanism contains all of the required flags. + if ((requiredFlags & ~mechanismFlags) == 0) + { + //log.debug("Mechanism " + SUPPORTED_MECHANISMS[i] + " meets the required properties."); + validMechanisms.add(SUPPORTED_MECHANISMS[i]); + } + } + + String[] result = (String[]) validMechanisms.toArray(new String[validMechanisms.size()]); + + //log.debug("result = " + PrettyPrintingUtils.printArray(result)); + + return result; + } + + /** + * Transforms a set of Sasl properties, defined using the property names in javax.security.sasl.Sasl, into + * a bit mapped set of property flags encoded using the bit mapping constants defined in this class. + * + * @param properties The Sasl properties to bit map. + * + * @return A set of bit mapped properties encoded in an integer. + */ + private int bitMapSaslProperties(Map properties) + { + //log.debug("private int bitMapSaslProperties(Map properties = " + properties + "): called"); + + int result = 0; + + // No flags set if no properties are set. + if (properties == null) + { + return result; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOPLAINTEXT))) + { + result |= NOPLAINTEXT; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOACTIVE))) + { + result |= NOACTIVE; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NODICTIONARY))) + { + result |= NODICTIONARY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_NOANONYMOUS))) + { + result |= NOANONYMOUS; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_FORWARD_SECRECY))) + { + result |= FORWARD_SECRECY; + } + + if ("true".equalsIgnoreCase((String) properties.get(Sasl.POLICY_PASS_CREDENTIALS))) + { + result |= PASS_CREDENTIALS; + } + + return result; + } + + /** + * Uses the specified call back handler to query for the users log in name and password. + * + * @param prefix A prefix to prepend onto the username and password queries. + * @param authorizationId The default autorhization name. + * @param cbh The call back handler. + * + * @return The username and password from the callback. + * + * @throws SaslException If the callback fails for any reason. + */ + private Object[] getUserInfo(String prefix, String authorizationId, CallbackHandler cbh) throws SaslException + { + // Check that the callback handler is defined. + if (cbh == null) + { + throw new SaslException("Callback handler to get username/password required."); + } + + try + { + String userPrompt = prefix + " authentication id: "; + String passwdPrompt = prefix + " password: "; + + NameCallback ncb = + (authorizationId == null) ? new NameCallback(userPrompt) : new NameCallback(userPrompt, authorizationId); + PasswordCallback pcb = new PasswordCallback(passwdPrompt, false); + + // Ask the call back handler to get the users name and password. + cbh.handle(new Callback[] { ncb, pcb }); + + char[] pw = pcb.getPassword(); + + byte[] bytepw; + String authId; + + if (pw != null) + { + bytepw = new String(pw).getBytes("UTF8"); + pcb.clearPassword(); + } + else + { + bytepw = null; + } + + authId = ncb.getName(); + + return new Object[] { authId, bytepw }; + } + catch (IOException e) + { + throw new SaslException("Cannot get password.", e); + } + catch (UnsupportedCallbackException e) + { + throw new SaslException("Cannot get userid/password.", e); + } + } +} diff --git a/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java new file mode 100644 index 0000000000..4f890ffed5 --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java @@ -0,0 +1,347 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.sasl; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the CRAM-MD5 SASL mechanism. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Concatenate the user id and password hashed by a challenge string as the challenge response. + *
Ensure password is wiped once a challenge has been processed. + *
+ */ +public class CramMD5Client implements SaslClient +{ + /** Defines the HMAC block size. */ + private static final int MD5_BLOCKSIZE = 64; + + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authenticationId; + private byte[] password; + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + CramMD5Client(String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("CRAM: authentication id and password must be specified"); + } + + // Keep the log in credentials. + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "CRAM-MD5"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * evaluateChallenge() with an empty array to get the initial response. + * + *

CRAM-MD5 has no intial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return false; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + *

The initial response for the SASL command, for the CRAM-MD5 mechanism is the concatenation of authentication + * ID and password HMAC-MD5 hashed by the server challenge. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("CRAM-MD5 authentication already completed."); + } + + // Check if the password is null, this will be the case if a previous attempt to authenticated failed. + if (password == null) + { + throw new IllegalStateException("CRAM-MD5 authentication previously aborted due to error."); + } + + // Generate a keyed-MD5 digest from the user's password keyed by the challenge bytes. + try + { + String digest = hmac_md5(password, challenge); + String result = authenticationId + " " + digest; + + completed = true; + + return result.getBytes("UTF8"); + } + catch (java.security.NoSuchAlgorithmException e) + { + throw new SaslException("MD5 algorithm not available on platform", e); + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("UTF8 not available on platform", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange + * has completed (i.e., when isComplete() returns true) and only if the authentication exchange has + * negotiated integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is + * thrown. + * + *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. offset and len specify the portion of incoming + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes from the server. + * @param offset The starting position at incoming of the bytes to use. + * @param len The number of bytes from incoming to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. offset and len specify the portion of + * outgoing to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at outgoing of the bytes to use. + * @param len The number of bytes from outgoing to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("CRAM-MD5 does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("CRAM-MD5 authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { } + + /* + * Hashes its input arguments according to HMAC-MD5 (RFC 2104) and returns the resulting digest in its ASCII + * representation. + * + *

The HMAC-MD5 function is described as follows: + *

+     *     MD5(key XOR opad, MD5(key XOR ipad, text))
+     * 
+ * + *

Where key is an n byte key, ipad is the byte 0x36 repeated 64 times, opad is the byte 0x5c repeated 64 times + * and text is the data to be protected. + * + * @param key The key to hash by. + * @param text The plain text to hash. + * + * @return The hashed text. + * + * @throws NoSuchAlgorithmException If the Java platform does not supply an MD5 implementation. + */ + private static final String hmac_md5(byte[] key, byte[] text) throws NoSuchAlgorithmException + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + /* digest the key if longer than 64 bytes */ + if (key.length > 64) + { + key = md5.digest(key); + } + + byte[] ipad = new byte[MD5_BLOCKSIZE]; /* inner padding */ + byte[] opad = new byte[MD5_BLOCKSIZE]; /* outer padding */ + byte[] digest; + int i; + + /* store key in pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + for (; i < key.length; i++) + { + ipad[i] = key[i]; + opad[i] = key[i]; + } + + ipad[i] = 0x00; + opad[i] = 0x00; + } + + /* XOR key with pads */ + for (i = 0; i < MD5_BLOCKSIZE; i++) + { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + /* inner MD5 */ + md5.update(ipad); + md5.update(text); + digest = md5.digest(); + + /* outer MD5 */ + md5.update(opad); + md5.update(digest); + digest = md5.digest(); + + // Get character representation of digest + StringBuffer digestString = new StringBuffer(); + + for (i = 0; i < digest.length; i++) + { + if ((digest[i] & 0x000000ff) < 0x10) + { + digestString.append("0" + Integer.toHexString(digest[i] & 0x000000ff)); + } + else + { + digestString.append(Integer.toHexString(digest[i] & 0x000000ff)); + } + } + + return (digestString.toString()); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java new file mode 100644 index 0000000000..f5a9f150bb --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java @@ -0,0 +1,275 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.sasl; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * Implements the PLAIN SASL mechanism. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Concatenate the user id and password in plain text as the challenge respose. + *
Ensure password is wiped once a challenge has been processed. + *
+ */ +public class PlainClient implements SaslClient +{ + /** Flag used to indicate that the authentication has completed. */ + private boolean completed = false; + + private String authorizationId; + private String authenticationId; + private byte[] password; + + private static byte SEPERATOR = 0; // US-ASCII + + /** + * Creates a PLAIN SASL client for an authorization id, authentication id and password. + * + * @param authorizationId The authorization id. May be null. + * @param authenticationId The authentication id. + * @param password The password. + * + * @throws SaslException If the authentication id or password is null. + */ + PlainClient(String authorizationId, String authenticationId, byte[] password) throws SaslException + { + // Check that a username and password are specified. + if ((authenticationId == null) || (password == null)) + { + throw new SaslException("PLAIN: authentication ID and password must be specified"); + } + + // Keep the log in credentials. + this.authorizationId = authorizationId; + this.authenticationId = authenticationId; + this.password = password; + } + + /** + * Returns the IANA-registered mechanism name of this SASL client. (e.g. "CRAM-MD5", "GSSAPI"). + * + * @return A non-null string representing the IANA-registered mechanism name. + */ + public String getMechanismName() + { + return "PLAIN"; + } + + /** + * Determines whether this mechanism has an optional initial response. If true, caller should call + * evaluateChallenge() with an empty array to get the initial response. + * + * @return true if this mechanism has an initial response. + */ + public boolean hasInitialResponse() + { + return true; + } + + /** + * Evaluates the challenge data and generates a response. If a challenge is received from the server during the + * authentication process, this method is called to prepare an appropriate next response to submit to the server. + * + *

The initial response for the SASL command, for the PLAIN mechanism is the concatenation of authorization ID, + * authentication ID and password, with each component separated by the US-ASCII byte. + * + * @param challenge The non-null challenge sent from the server. The challenge array may have zero length. + * + * @return The possibly null reponse to send to the server. It is null if the challenge accompanied a "SUCCESS" + * status and the challenge only contains data for the client to update its state and no response + * needs to be sent to the server. The response is a zero-length byte array if the client is to send a + * response with no data. + * + * @throws javax.security.sasl.SaslException If an error occurred while processing the challenge or generating a + * response. + */ + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // Check that the authentication has not already been performed. + if (completed) + { + throw new IllegalStateException("PLAIN authentication already completed"); + } + + try + { + // Get the authorization and authentication ids in bytes. + byte[] authorizationBytes = (authorizationId != null) ? authorizationId.getBytes("UTF8") : null; + byte[] authenticationBytes = authenticationId.getBytes("UTF8"); + + // Create an array big enough to hold the results. + byte[] result = + new byte[password.length + authenticationBytes.length + 2 + + ((authorizationBytes == null) ? 0 : authorizationBytes.length)]; + + // Copy the authorization id, authentication id and password into the results. + int pos = 0; + if (authorizationBytes != null) + { + System.arraycopy(authorizationBytes, 0, result, 0, authorizationBytes.length); + pos = authorizationBytes.length; + } + + result[pos++] = SEPERATOR; + System.arraycopy(authenticationBytes, 0, result, pos, authenticationBytes.length); + + pos += authenticationBytes.length; + result[pos++] = SEPERATOR; + + System.arraycopy(password, 0, result, pos, password.length); + + completed = true; + + return result; + } + catch (java.io.UnsupportedEncodingException e) + { + throw new SaslException("Cannot get UTF-8 encoding of ids", e); + } + finally + { + clearPassword(); + } + } + + /** + * Determines whether the authentication exchange has completed. This method may be called at any time, but + * typically, it will not be called until the caller has received indication from the server (in a protocol-specific + * manner) that the exchange has completed. + * + * @return true if the authentication exchange has completed; false otherwise. + */ + public boolean isComplete() + { + return completed; + } + + /** + * Unwraps a byte array received from the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

incoming is the contents of the SASL buffer as defined in RFC 2222 without the leading four octet + * field that represents the length. offset and len specify the portion of incoming + * to use. + * + * @param incoming A non-null byte array containing the encoded bytes + * from the server. + * @param offset The starting position at incoming of the bytes to use. + * @param len The number of bytes from incoming to use. + * + * @return A non-null byte array containing the decoded bytes. + * + * @throws javax.security.sasl.SaslException If incoming cannot be successfully unwrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Wraps a byte array to be sent to the server. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true) and only if the authentication exchange has negotiated + * integrity and/or privacy as the quality of protection; otherwise, an IllegalStateException is thrown. + * + *

The result of this method will make up the contents of the SASL buffer as defined in RFC 2222 without the + * leading four octet field that represents the length. offset and len specify the portion of + * outgoing to use. + * + * @param outgoing A non-null byte array containing the bytes to encode. + * @param offset The starting position at outgoing of the bytes to use. + * @param len The number of bytes from outgoing to use. + * + * @return A non-null byte array containing the encoded bytes. + * + * @throws javax.security.sasl.SaslException If outgoing cannot be successfully wrapped. + * @throws IllegalStateException If the authentication exchange has not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. + */ + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("PLAIN does not support quality of protection."); + } + + /** + * Retrieves the negotiated property. This method can be called only after the authentication exchange has + * completed (i.e., when isComplete() returns true); otherwise, an IllegalStateException is thrown. + * + * @param propName The non-null property name. + * + * @return The value of the negotiated property. If null, the property was not negotiated or is not applicable to + * this mechanism. + * + * @throws IllegalStateException If this authentication exchange has not completed. + */ + public Object getNegotiatedProperty(String propName) + { + if (completed) + { + if (propName.equals(Sasl.QOP)) + { + return "auth"; + } + else + { + return null; + } + } + else + { + throw new IllegalStateException("PLAIN authentication not completed"); + } + } + + /** + * Disposes of any system resources or security-sensitive information the SaslClient might be using. Invoking this + * method invalidates the SaslClient instance. This method is idempotent. + * + * @throws javax.security.sasl.SaslException If a problem was encountered while disposing the resources. + */ + public void dispose() throws SaslException + { + clearPassword(); + } + + /** + * Overwrites the password with zeros. + */ + private void clearPassword() + { + if (password != null) + { + // Zero out password. + for (int i = 0; i < password.length; i++) + { + password[i] = (byte) 0; + } + + password = null; + } + } +} diff --git a/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java new file mode 100644 index 0000000000..ef075a0cad --- /dev/null +++ b/RC6/qpid/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.sasl; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.log4j.Logger; + +/** + * A SASL provider for Java 1.4. Declares the capabilities of this implementation, which supports PLAIN and CRAM-MD5 + * client implementations only. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Declare PLAIN SASL support. + *
Declare CRAM-MD5 SASL support. + *
+ */ +public class Provider extends java.security.Provider +{ + //Logger log = Logger.getLogger(Provider.class); + + private static final String info = "Qpid SASL provider" + "(implements client mechanisms for: PLAIN, CRAM-MD5)"; + + public Provider() + { + super("QpidSASL", 1.4, info); + + //log.debug("public Provider(): called"); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + put("SaslClientFactory.PLAIN", "org.apache.qpid.sasl.ClientFactoryImpl"); + put("SaslClientFactory.CRAM-MD5", "org.apache.qpid.sasl.ClientFactoryImpl"); + + return null; + } + }); + } +} diff --git a/RC6/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java b/RC6/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java new file mode 100644 index 0000000000..77cc79bc49 --- /dev/null +++ b/RC6/qpid/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.integration.client; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.client.AMQConnection; + +/** + * Implements a trivial broker connection test, to a broker on the default port on localhost, to check that SASL + * authentication works. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Check that a connection to a broker can be established. + *
+ */ +public class ConnectionTest extends TestCase +{ + private String BROKER_URL = "tcp://localhost:5672"; + + public ConnectionTest(String name) + { + super(name); + } + + /** Check that a connection to a broker can be established. */ + public void testConnection() throws Exception + { + // Open a connection to the broker and close it again. + AMQConnection conn = new AMQConnection(BROKER_URL, "guest", "guest", "clientid", "test"); + conn.close(); + } + + protected void setUp() + { + NDC.push(getName()); + } + + protected void tearDown() + { + NDC.pop(); + } +} diff --git a/RC6/qpid/java/client/build.xml b/RC6/qpid/java/client/build.xml new file mode 100644 index 0000000000..e826df9c74 --- /dev/null +++ b/RC6/qpid/java/client/build.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/client/example/bin/README.txt b/RC6/qpid/java/client/example/bin/README.txt new file mode 100644 index 0000000000..9a1ce91d41 --- /dev/null +++ b/RC6/qpid/java/client/example/bin/README.txt @@ -0,0 +1,11 @@ += Qpid Java Examples = + +For more information read ../README.txt. + +== The Verify All Script == + +The verify_all script will run Java examples against itself and against the C++ +and Python examples. The success of the script is determined by comparing its +output against what is expected. + +This script uses the verify script found in qpid/cpp/examples. diff --git a/RC6/qpid/java/client/example/bin/set_classpath.bat b/RC6/qpid/java/client/example/bin/set_classpath.bat new file mode 100644 index 0000000000..d528967024 --- /dev/null +++ b/RC6/qpid/java/client/example/bin/set_classpath.bat @@ -0,0 +1,50 @@ +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. + +@REM Helper script to set classpath for running Qpid example classes +@REM NB: You must add the Qpid client and common jars to your CLASSPATH +@REM before running this script + +@echo off + +if "%QPID_HOME%" == "" GOTO ERROR_QPID_HOME + +set QPIDLIB=%QPID_HOME%\lib + +if "%CLASSPATH%" == "" GOTO ERROR_CLASSPATH + +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\backport-util-concurrent-2.2.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\geronimo-jms_1.1_spec-1.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-collections-3.1.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-configuration-1.2.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-cli-1.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-lang-2.1.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-logging-api-1.0.4.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\commons-logging-1.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\log4j-1.2.12.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-core-1.0.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-filter-ssl-1.0.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\mina-java5-1.0.0.jar +set CLASSPATH=%CLASSPATH%;%QPIDLIB%\slf4j-simple-1.0.jar + +GOTO END + +:ERROR_CLASSPATH +Echo Please set set your CLASSPATH variable to include the Qpid client and common jars. Exiting .... +:ERROR_QPID_HOME +Echo Please set QPID_HOME variable. Exiting .... +:END diff --git a/RC6/qpid/java/client/example/bin/set_classpath.sh b/RC6/qpid/java/client/example/bin/set_classpath.sh new file mode 100755 index 0000000000..89e9bc8242 --- /dev/null +++ b/RC6/qpid/java/client/example/bin/set_classpath.sh @@ -0,0 +1,83 @@ +#!/bin/sh -xv +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Helper script to set classpath for running Qpid example classes +# NB: You must add the Qpid client and common jars to your CLASSPATH +# before running this script + + +cygwin=false +if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then + cygwin=true +fi + +#Should have set the QPID_HOME var after install to the working dir e.g. home/qpid/qpid-1.0-incubating-M2-SNAPSHOT +if [ "$QPID_HOME" = "" ] ; then + echo "ERROR: Please set QPID_HOME variable. Exiting ...." + exit 1 +else + QPIDLIB=$QPID_HOME/lib +fi + +if $cygwin; then + QPIDLIB=$(cygpath -w $QPIDLIB) +fi + +if [ "$CLASSPATH" = "" ] ; then + echo "ERROR: Please set set your CLASSPATH variable to include the Qpid client and common jars. Exiting ...." + exit 2 +fi + +#Converts paths for cygwin if req +#Some nasty concatenation to get round cygpath line limits +if $cygwin; then + SEP=";" + CLASSPATH=`cygpath -w $CLASSPATH` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/backport-util-concurrent-2.2.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/geronimo-jms_1.1_spec-1.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-collections-3.1.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-configuration-1.2.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-cli-1.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-lang-2.1.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-logging-api-1.0.4.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/commons-logging-1.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/log4j-1.2.12.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-core-1.0.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-filter-ssl-1.0.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/mina-java5-1.0.0.jar` + CLASSPATH=$CLASSPATH$SEP`cygpath -w $QPIDLIB/slf4j-simple-1.0.jar` + export CLASSPATH +else + CLASSPATH=$CLASSPATH:$QPIDLIB/backport-util-concurrent-2.2.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/geronimo-jms_1.1_spec-1.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-collections-3.1.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-configuration-1.2.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-cli-1.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-lang-2.1.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-logging-api-1.0.4.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/commons-logging-1.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/log4j-1.2.12.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/mina-core-1.0.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/mina-filter-ssl-1.0.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/mina-java5-1.0.0.jar + CLASSPATH=$CLASSPATH:$QPIDLIB/slf4j-simple-1.0.jar + export CLASSPATH +fi + diff --git a/RC6/qpid/java/client/example/bin/verify_all b/RC6/qpid/java/client/example/bin/verify_all new file mode 100644 index 0000000000..0212e7d819 --- /dev/null +++ b/RC6/qpid/java/client/example/bin/verify_all @@ -0,0 +1,59 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export QPID_SRC_HOME=$(cd "$(dirname $0)/../../../.."; pwd) +export CPP=$QPID_SRC_HOME/cpp/examples/examples +export PYTHON=$QPID_SRC_HOME/python/examples +export JAVA=$QPID_SRC_HOME/java/client/example/src/main/java/org/apache/qpid/example/jmsexample + +export AMQP_SPEC=$QPID_SRC_HOME/specs/amqp.0-10.xml +export PYTHONPATH=$QPID_SRC_HOME/python/ + +trap cleanup EXIT + +run_broker(){ + $QPID_SRC_HOME/cpp/src/qpidd -d --no-data-dir --auth no +} + +stop_broker(){ + $QPID_SRC_HOME/cpp/src/qpidd -q +} + +cleanup(){ + if [ -e /tmp/qpidd.5672.pid ]; then + stop_broker + fi + find $CPP -name '*.out' | xargs rm -f + find $PYTHON -name '*.out' | xargs rm -f + find $JAVA -name '*.out' | xargs rm -f +} + +QPID_LIBS=`find $QPID_SRC_HOME/java/build/lib -name '*.jar' | tr '\n' ":"` +export CLASSPATH=$QPID_LIBS:$CLASSPATH + +verify=$QPID_SRC_HOME/cpp/examples/verify + +for script in $(find $JAVA -name 'verify*' -not -path '*.svn' -not -name '*.*') +do + run_broker + $verify $script + stop_broker +done + diff --git a/RC6/qpid/java/client/example/build.xml b/RC6/qpid/java/client/example/build.xml new file mode 100644 index 0000000000..8bcd59d829 --- /dev/null +++ b/RC6/qpid/java/client/example/build.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/RC6/qpid/java/client/example/source-jar.xml b/RC6/qpid/java/client/example/source-jar.xml new file mode 100644 index 0000000000..60451448b8 --- /dev/null +++ b/RC6/qpid/java/client/example/source-jar.xml @@ -0,0 +1,35 @@ + + + + source + + jar + + false + + + src/main/java + + + + diff --git a/RC6/qpid/java/client/example/src/main/java/README.txt b/RC6/qpid/java/client/example/src/main/java/README.txt new file mode 100644 index 0000000000..c8fe6a76ad --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/README.txt @@ -0,0 +1,17 @@ +In order to use the runSample script, you are required to set two environment +variables, QPID_HOME and QPID_SAMPLE. If not the default values will be used. + +QPID_HOME +--------- +This is the distribution directory. You would have set the QPID_HOME when you +started the Qpid Java broker. + +default: /usr/share/java/ + +QPID_SAMPLE +----------- +This is the parent directory of the directory in which you find the runSample.sh +(Ex:- $QPID_SRC_HOME/java/client/example/src/main) + +default: /usr/share/doc/rhm-0.2 + diff --git a/RC6/qpid/java/client/example/src/main/java/log4j.xml b/RC6/qpid/java/client/example/src/main/java/log4j.xml new file mode 100644 index 0000000000..0b38b14c02 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/log4j.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DeclareQueue.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DeclareQueue.java new file mode 100755 index 0000000000..38073cb7f2 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DeclareQueue.java @@ -0,0 +1,55 @@ +package org.apache.qpid.example.amqpexample.direct; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.Session; + +/** + * This creates a queue a queue and binds it to the + * amq.direct exchange + * + */ +public class DeclareQueue +{ + + public static void main(String[] args) + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // declare and bind queue + session.queueDeclare("message_queue", null, null); + session.exchangeBind("message_queue", "amq.direct", "routing_key", null); + + // confirm completion + session.sync(); + + //cleanup + session.close(); + con.close(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DirectProducer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DirectProducer.java new file mode 100755 index 0000000000..2234eb22da --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/DirectProducer.java @@ -0,0 +1,67 @@ +package org.apache.qpid.example.amqpexample.direct; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; + +import org.apache.qpid.api.Message; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.Header; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.Session; + +public class DirectProducer +{ + + public static void main(String[] args) + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + DeliveryProperties deliveryProps = new DeliveryProperties(); + deliveryProps.setRoutingKey("routing_key"); + + for (int i=0; i<10; i++) + { + session.messageTransfer("amq.direct", MessageAcceptMode.EXPLICIT,MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProps), + "Message " + i); + } + + session.messageTransfer("amq.direct", MessageAcceptMode.EXPLICIT, MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProps), + "That's all, folks!"); + + // confirm completion + session.sync(); + + //cleanup + session.close(); + con.close(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/Listener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/Listener.java new file mode 100755 index 0000000000..93bb097268 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/direct/Listener.java @@ -0,0 +1,103 @@ +package org.apache.qpid.example.amqpexample.direct; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageCreditUnit; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.SessionListener; + +/** + * This listens to messages on a queue and terminates + * when it sees the final message + * + */ +public class Listener implements SessionListener +{ + + public void opened(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) + { + System.out.println("Message: " + xfr); + } + + public void exception(Session ssn, SessionException exc) + { + exc.printStackTrace(); + } + + public void closed(Session ssn) {} + + /** + * This sends 10 messages to the + * amq.direct exchange using the + * routing key as "routing_key" + * + */ + public static void main(String[] args) throws InterruptedException + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // Create an instance of the listener + Listener listener = new Listener(); + session.setSessionListener(listener); + + // create a subscription + session.messageSubscribe("message_queue", + "listener_destination", + MessageAcceptMode.NONE, + MessageAcquireMode.PRE_ACQUIRED, + null, 0, null); + + + // issue credits + // XXX + session.messageFlow("listener_destination", MessageCreditUnit.BYTE, Session.UNLIMITED_CREDIT); + session.messageFlow("listener_destination", MessageCreditUnit.MESSAGE, 11); + + // confirm completion + session.sync(); + + // wait to receive all the messages + System.out.println("Waiting 100 seconds for messages from listener_destination"); + Thread.sleep(100*1000); + System.out.println("Shutting down listener for listener_destination"); + session.messageCancel("listener_destination"); + + //cleanup + session.close(); + con.close(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/DeclareQueue.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/DeclareQueue.java new file mode 100755 index 0000000000..9c3ec2fb3b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/DeclareQueue.java @@ -0,0 +1,55 @@ +package org.apache.qpid.example.amqpexample.fanout; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.Session; + +/** + * This creates a queue a queue and binds it to the + * amq.direct exchange + * + */ +public class DeclareQueue +{ + + public static void main(String[] args) + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // declare and bind queue + session.queueDeclare("message_queue", null, null); + session.exchangeBind("message_queue", "amq.fanout",null, null); + + // confirm completion + session.sync(); + + //cleanup + session.close(); + con.close(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/FannoutProducer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/FannoutProducer.java new file mode 100755 index 0000000000..39d34713c6 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/FannoutProducer.java @@ -0,0 +1,66 @@ +package org.apache.qpid.example.amqpexample.fanout; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.Header; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.Session; + +public class FannoutProducer +{ + /** + * This sends 10 messages to the + * amq.fannout exchange + */ + public static void main(String[] args) + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + DeliveryProperties deliveryProps = new DeliveryProperties(); + deliveryProps.setRoutingKey("routing_key"); + + for (int i=0; i<10; i++) + { + session.messageTransfer("amq.fanout", MessageAcceptMode.EXPLICIT, MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProps), "Message " + i); + } + + session.messageTransfer("amq.fanout", MessageAcceptMode.EXPLICIT, MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProps), + "That's all, folks!"); + + // confirm completion + session.sync(); + + //cleanup + session.close(); + con.close(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/Listener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/Listener.java new file mode 100755 index 0000000000..4c72ce75a5 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/fanout/Listener.java @@ -0,0 +1,103 @@ +package org.apache.qpid.example.amqpexample.fanout; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageCreditUnit; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.SessionListener; + +/** + * This listens to messages on a queue and terminates + * when it sees the final message + * + */ +public class Listener implements SessionListener +{ + + public void opened(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) + { + System.out.println("Message: " + xfr); + } + + public void exception(Session ssn, SessionException exc) + { + exc.printStackTrace(); + } + + public void closed(Session ssn) {} + + /** + * This sends 10 messages to the + * amq.direct exchange using the + * routing key as "routing_key" + * + */ + public static void main(String[] args) throws InterruptedException + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // Create an instance of the listener + Listener listener = new Listener(); + session.setSessionListener(listener); + + // create a subscription + session.messageSubscribe("message_queue", + "listener_destination", + MessageAcceptMode.NONE, + MessageAcquireMode.PRE_ACQUIRED, + null, 0, null); + + + // issue credits + // XXX + session.messageFlow("listener_destination", MessageCreditUnit.BYTE, Session.UNLIMITED_CREDIT); + session.messageFlow("listener_destination", MessageCreditUnit.MESSAGE, 11); + + // confirm completion + session.sync(); + + // check to see if we have received all the messages + System.out.println("Waiting 100 seconds for messages from listener_destination"); + Thread.sleep(100*1000); + System.out.println("Shutting down listener for listener_destination"); + session.messageCancel("listener_destination"); + + //cleanup + session.close(); + con.close(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicListener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicListener.java new file mode 100755 index 0000000000..5e6d3c6f69 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicListener.java @@ -0,0 +1,112 @@ +package org.apache.qpid.example.amqpexample.pubsub; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageCreditUnit; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.SessionListener; + + +public class TopicListener implements SessionListener +{ + + public void opened(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) + { + DeliveryProperties dp = xfr.getHeader().get(DeliveryProperties.class); + System.out.println("Message: " + xfr + " with routing_key " + dp.getRoutingKey()); + } + + public void exception(Session ssn, SessionException exc) + { + exc.printStackTrace(); + } + + public void closed(Session ssn) {} + + public void prepareQueue(Session session,String queueName,String bindingKey) + { + session.queueDeclare(queueName, null, null, Option.EXCLUSIVE, Option.AUTO_DELETE); + session.exchangeBind(queueName, "amq.topic", bindingKey, null); + session.exchangeBind(queueName, "amq.topic", "control", null); + + session.messageSubscribe(queueName, queueName, + MessageAcceptMode.NONE, + MessageAcquireMode.PRE_ACQUIRED, + null, 0, null); + // issue credits + // XXX: need to be able to set to null + session.messageFlow(queueName, MessageCreditUnit.BYTE, Session.UNLIMITED_CREDIT); + session.messageFlow(queueName, MessageCreditUnit.MESSAGE, 24); + } + + public void cancelSubscription(Session session,String dest) + { + session.messageCancel(dest); + } + + public static void main(String[] args) throws InterruptedException + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // Create an instance of the listener + TopicListener listener = new TopicListener(); + session.setSessionListener(listener); + + listener.prepareQueue(session,"usa", "usa.#"); + listener.prepareQueue(session,"europe", "europe.#"); + listener.prepareQueue(session,"news", "#.news"); + listener.prepareQueue(session,"weather", "#.weather"); + + // confirm completion + session.sync(); + + System.out.println("Waiting 100 seconds for messages"); + Thread.sleep(100*1000); + + System.out.println("Shutting down listeners"); + listener.cancelSubscription(session,"usa"); + listener.cancelSubscription(session,"europe"); + listener.cancelSubscription(session,"news"); + listener.cancelSubscription(session,"weather"); + + //cleanup + session.close(); + con.close(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicPublisher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicPublisher.java new file mode 100755 index 0000000000..facf08eeca --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/amqpexample/pubsub/TopicPublisher.java @@ -0,0 +1,80 @@ +package org.apache.qpid.example.amqpexample.pubsub; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.Header; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.Session; + +public class TopicPublisher +{ + + public void publishMessages(Session session, String routing_key) + { + // Set the routing key once, we'll use the same routing key for all + // messages. + + DeliveryProperties deliveryProps = new DeliveryProperties(); + deliveryProps.setRoutingKey(routing_key); + + for (int i=0; i<5; i++) { + session.messageTransfer("amq.topic", MessageAcceptMode.EXPLICIT, MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProps), "Message " + i); + } + + } + + public void noMoreMessages(Session session) + { + session.messageTransfer("amq.topic", MessageAcceptMode.EXPLICIT, MessageAcquireMode.PRE_ACQUIRED, + new Header(new DeliveryProperties().setRoutingKey("control")), + "That's all, folks!"); + } + + public static void main(String[] args) + { + // Create connection + Connection con = new Connection(); + con.connect("localhost", 5672, "test", "guest", "guest",false); + + // Create session + Session session = con.createSession(0); + + // Create an instance of the listener + TopicPublisher publisher = new TopicPublisher(); + + publisher.publishMessages(session, "usa.news"); + publisher.publishMessages(session, "usa.weather"); + publisher.publishMessages(session, "europe.news"); + publisher.publishMessages(session, "europe.weather"); + + // confirm completion + session.sync(); + + //cleanup + session.close(); + con.close(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Consumer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Consumer.java new file mode 100644 index 0000000000..f84b16f485 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Consumer.java @@ -0,0 +1,152 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.direct; + +import java.util.Properties; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * The example creates a MessageConsumer on the specified + * Queue which is used to synchronously consume messages. + */ +public class Consumer +{ + /** + * Used in log output. + */ + private static final String CLASS = "Consumer"; + + + /** + * Run the message consumer example. + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Consumer syncConsumer = new Consumer(); + syncConsumer.runTest(); + } + + /** + * Start the example. + */ + private void runTest() + { + try + { + // Load JNDI properties + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("direct.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + // look up destination + Destination destination = (Destination)ctx.lookup("directQueue"); + + // Lookup the connection factory + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + // create the connection + Connection connection = conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a MessageConsumer + System.out.println(CLASS + ": Creating a MessageConsumer"); + MessageConsumer messageConsumer = session.createConsumer(destination); + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so MessageConsumer can receive messages"); + connection.start(); + + // Cycle round until all the messages are consumed. + Message message; + boolean end = false; + while (!end) + { + message = messageConsumer.receive(); + String text; + if (message instanceof TextMessage) + { + text = ((TextMessage) message).getText(); + } + else + { + byte[] body = new byte[(int) ((BytesMessage) message).getBodyLength()]; + ((BytesMessage) message).readBytes(body); + text = new String(body); + } + if (text.equals("That's all, folks!")) + { + System.out.println(CLASS + ": Received final message " + text); + end = true; + } + else + { + System.out.println(CLASS + ": Received message: " + text); + } + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Listener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Listener.java new file mode 100644 index 0000000000..d2e1180c9b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Listener.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.example.jmsexample.direct; + +import java.util.Properties; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * The example creates a MessageConsumer on the specified + * Queue and uses a MessageListener with this MessageConsumer + * in order to enable asynchronous delivery. + */ +public class Listener implements MessageListener +{ + /* Used in log output. */ + private static final String CLASS = "Listener"; + + /** + * An object to synchronize on. + */ + private final static Object _lock = new Object(); + + /** + * A boolean to indicate a clean finish. + */ + private static boolean _finished = false; + + /** + * A boolean to indicate an unsuccesful finish. + */ + private static boolean _failed = false; + + + /** + * Run the message consumer example. + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Listener listener = new Listener(); + listener.runTest(); + } + + /** + * Start the example. + */ + private void runTest() + { + try + { + // Load JNDI properties + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("direct.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + // look up destination + Destination destination = (Destination)ctx.lookup("directQueue"); + + // Lookup the connection factory + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + // create the connection + Connection connection = conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses + // the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a MessageConsumer + System.out.println(CLASS + ": Creating a MessageConsumer"); + + MessageConsumer messageConsumer = session.createConsumer(destination); + + // Set a message listener on the messageConsumer + messageConsumer.setMessageListener(this); + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so MessageConsumer can receive messages"); + connection.start(); + + // Wait for the messageConsumer to have received all the messages it needs + synchronized (_lock) + { + while (!_finished && !_failed) + { + _lock.wait(); + } + } + + // If the MessageListener abruptly failed (probably due to receiving a non-text message) + if (_failed) + { + System.out.println(CLASS + ": This sample failed as it received unexpected messages"); + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } + + /** + * This method is required by the MessageListener interface. It + * will be invoked when messages are available. + * After receiving the finish message (That's all, folks!) it releases a lock so that the + * main program may continue. + * + * @param message The message. + */ + public void onMessage(Message message) + { + try + { + String text; + if (message instanceof TextMessage) + { + text = ((TextMessage) message).getText(); + } + else + { + byte[] body = new byte[(int) ((BytesMessage) message).getBodyLength()]; + ((BytesMessage) message).readBytes(body); + text = new String(body); + } + if (text.equals("That's all, folks!")) + { + System.out.println(CLASS + ": Received final message " + text); + synchronized (_lock) + { + _finished = true; + _lock.notifyAll(); + } + } + else + { + System.out.println(CLASS + ": Received message: " + text); + } + } + catch (JMSException exp) + { + System.out.println(CLASS + ": Caught an exception handling a received message"); + exp.printStackTrace(); + synchronized (_lock) + { + _failed = true; + _lock.notifyAll(); + } + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Producer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Producer.java new file mode 100644 index 0000000000..259756764c --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/Producer.java @@ -0,0 +1,138 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.direct; + +import java.util.Properties; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * Message producer example, sends message to a queue. + */ +public class Producer +{ + /* Used in log output. */ + private static final String CLASS = "Producer"; + + private int numMessages = 10; + private short deliveryMode = DeliveryMode.NON_PERSISTENT; + + /** + * Create a Producer client. + */ + public Producer () + { + } + + /** + * Run the message producer example. + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Producer producer = new Producer(); + producer.runTest(); + } + + private void runTest() + { + try + { + + // Load JNDI properties + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("direct.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + // look up destination + Destination destination = (Destination)ctx.lookup("directQueue"); + + // Lookup the connection factory + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + // create the connection + Connection connection = conFac.createConnection(); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + e.printStackTrace(); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // lookup the queue + //Queue destination = session.createQueue(_queueName); + + // Create a Message producer + System.out.println(CLASS + ": Creating a Message Producer"); + MessageProducer messageProducer = session.createProducer(destination); + + // Create a Message + TextMessage message; + System.out.println(CLASS + ": Creating a TestMessage to send to the destination"); + + // Loop to publish the requested number of messages. + for (int i = 1; i < numMessages + 1; i++) + { + // NOTE: We have NOT HAD TO START THE CONNECTION TO BEGIN SENDING messages, + // this is different to the consumer end as a CONSUMERS CONNECTIONS MUST BE STARTED BEFORE RECEIVING. + message = session.createTextMessage("Message " + i); + System.out.println(CLASS + ": Sending message: " + i); + messageProducer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + } + + // And send a final message to indicate termination. + message = session.createTextMessage("That's all, folks!"); + messageProducer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + + // Close the connection to the broker + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + exp.printStackTrace(); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/direct.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/direct.properties new file mode 100644 index 0000000000..a2f5843e7a --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/direct.properties @@ -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. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# Register an AMQP destination in JNDI +# destination.[jniName] = [BindingURL] +destination.directQueue = direct://amq.direct//message_queue?routingkey='routing_key' \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify new file mode 100644 index 0000000000..66b4b3c54e --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +cpp=$CPP/direct + +direct_consumer_java() +{ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Consumer +} + +direct_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Producer +} + +clients $cpp/declare_queues direct_producer_java direct_consumer_java +outputs $cpp/declare_queues.out ./direct_producer_java.out ./direct_consumer_java.out diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify.in new file mode 100644 index 0000000000..c87e5716c8 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify.in @@ -0,0 +1,35 @@ +==== declare_queues.out +==== direct_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== direct_consumer_java.out +Consumer: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Consumer: Creating a non-transacted, auto-acknowledged session +Consumer: Creating a MessageConsumer +Consumer: Starting connection so MessageConsumer can receive messages +Consumer: Received message: Message 1 +Consumer: Received message: Message 2 +Consumer: Received message: Message 3 +Consumer: Received message: Message 4 +Consumer: Received message: Message 5 +Consumer: Received message: Message 6 +Consumer: Received message: Message 7 +Consumer: Received message: Message 8 +Consumer: Received message: Message 9 +Consumer: Received message: Message 10 +Consumer: Received final message That's all, folks! +Consumer: Closing connection +Consumer: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java new file mode 100644 index 0000000000..d581c4c1aa --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/direct + +direct_consumer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Consumer +} + +clients $cpp/declare_queues $cpp/direct_producer direct_consumer_java +outputs $cpp/declare_queues.out $cpp/direct_producer.out ./direct_consumer_java.out + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java.in new file mode 100644 index 0000000000..b50692da1f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_cpp_java.in @@ -0,0 +1,20 @@ +==== declare_queues.out +==== direct_producer.out +==== direct_consumer_java.out +Consumer: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Consumer: Creating a non-transacted, auto-acknowledged session +Consumer: Creating a MessageConsumer +Consumer: Starting connection so MessageConsumer can receive messages +Consumer: Received message: Message 0 +Consumer: Received message: Message 1 +Consumer: Received message: Message 2 +Consumer: Received message: Message 3 +Consumer: Received message: Message 4 +Consumer: Received message: Message 5 +Consumer: Received message: Message 6 +Consumer: Received message: Message 7 +Consumer: Received message: Message 8 +Consumer: Received message: Message 9 +Consumer: Received final message That's all, folks! +Consumer: Closing connection +Consumer: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp new file mode 100644 index 0000000000..573cac6986 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/direct + +direct_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Producer +} + +clients $cpp/declare_queues direct_producer_java $cpp/listener +outputs $cpp/declare_queues.out ./direct_producer_java.out $cpp/listener.out + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp.in new file mode 100644 index 0000000000..946c19953f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_cpp.in @@ -0,0 +1,30 @@ +==== declare_queues.out +==== direct_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== listener.out +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: Message 10 +Message: That's all, folks! +Shutting down listener for message_queue diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python new file mode 100644 index 0000000000..61c033e969 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python @@ -0,0 +1,9 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/direct + +direct_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Producer +} + +clients $py/declare_queues.py direct_producer_java $py/direct_consumer.py +outputs $py/declare_queues.py.out ./direct_producer_java.out $py/direct_consumer.py.out diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python.in new file mode 100644 index 0000000000..e012405352 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_java_python.in @@ -0,0 +1,29 @@ +==== declare_queues.py.out +==== direct_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== direct_consumer.py.out +Message 1 +Message 2 +Message 3 +Message 4 +Message 5 +Message 6 +Message 7 +Message 8 +Message 9 +Message 10 +That's all, folks! diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java new file mode 100644 index 0000000000..4182331f3f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/direct + +direct_consumer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.direct.Consumer +} + +clients $py/declare_queues.py $py/direct_producer.py direct_consumer_java +outputs $py/declare_queues.py.out $py/direct_producer.py.out ./direct_consumer_java.out + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java.in new file mode 100644 index 0000000000..6a9c9fdd10 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/direct/verify_python_java.in @@ -0,0 +1,20 @@ +==== declare_queues.py.out +==== direct_producer.py.out +==== direct_consumer_java.out +Consumer: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Consumer: Creating a non-transacted, auto-acknowledged session +Consumer: Creating a MessageConsumer +Consumer: Starting connection so MessageConsumer can receive messages +Consumer: Received message: message 0 +Consumer: Received message: message 1 +Consumer: Received message: message 2 +Consumer: Received message: message 3 +Consumer: Received message: message 4 +Consumer: Received message: message 5 +Consumer: Received message: message 6 +Consumer: Received message: message 7 +Consumer: Received message: message 8 +Consumer: Received message: message 9 +Consumer: Received final message That's all, folks! +Consumer: Closing connection +Consumer: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Consumer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Consumer.java new file mode 100755 index 0000000000..daa1b10b6b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Consumer.java @@ -0,0 +1,165 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.fanout; + +import java.util.Properties; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * The example creates a MessageConsumer on the specified + * Queue which is used to synchronously consume messages. + */ +public class Consumer +{ + /** + * Used in log output. + */ + private static final String CLASS = "Consumer"; + + /** + * Create a Consumer client. + * + */ + public Consumer() + { + } + + /** + * Run the message consumer example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) throws Exception + { + if (args.length == 0) + { + throw new Exception("You need to specify the JNDI name for the queue"); + } + Consumer syncConsumer = new Consumer(); + syncConsumer.runTest(args[0]); + } + + /** + * Start the example. + */ + private void runTest(String queueName) + { + try + { + // Load JNDI properties + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("fanout.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + // look up destination + Destination destination = (Destination)ctx.lookup(queueName); + + // Lookup the connection factory + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + // create the connection + Connection connection = conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a MessageConsumer + System.out.println(CLASS + ": Creating a MessageConsumer"); + MessageConsumer messageConsumer = session.createConsumer(destination); + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so MessageConsumer can receive messages"); + connection.start(); + + // Cycle round until all the messages are consumed. + Message message; + boolean end = false; + while (!end) + { + message = messageConsumer.receive(); + String text; + if (message instanceof TextMessage) + { + text = ((TextMessage) message).getText(); + } + else + { + byte[] body = new byte[(int) ((BytesMessage) message).getBodyLength()]; + ((BytesMessage) message).readBytes(body); + text = new String(body); + } + if (text.equals("That's all, folks!")) + { + System.out.println(CLASS + ": Received final message " + text); + end = true; + } + else + { + System.out.println(CLASS + ": Received message: " + text); + } + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + exp.printStackTrace(); + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Listener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Listener.java new file mode 100755 index 0000000000..fb750693b2 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Listener.java @@ -0,0 +1,201 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.fanout; + +import java.util.Properties; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * The example creates a MessageConsumer on the specified + * Queue and uses a MessageListener with this MessageConsumer + * in order to enable asynchronous delivery. + */ +public class Listener implements MessageListener +{ + /* Used in log output. */ + private static final String CLASS = "Listener"; + + /** + * An object to synchronize on. + */ + private final Object _lock = new Object(); + + /** + * A boolean to indicate a clean finish. + */ + private boolean _finished = false; + + /** + * A boolean to indicate an unsuccesful finish. + */ + private boolean _failed = false; + + + + /** + * Run the message consumer example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) throws Exception + { + if (args.length == 0) + { + throw new Exception("You need to specify the JNDI name for the queue"); + } + Listener listener = new Listener(); + listener.runTest(args[0]); + } + + /** + * Start the example. + */ + private void runTest(String queueName) + { + try + { + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("fanout.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + Destination destination = (Destination)ctx.lookup(queueName); + + // Declare the connection + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + Connection connection = conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses + // the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a MessageConsumer + System.out.println(CLASS + ": Creating a MessageConsumer"); + + MessageConsumer messageConsumer = session.createConsumer(destination); + + // Set a message listener on the messageConsumer + messageConsumer.setMessageListener(this); + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so MessageConsumer can receive messages"); + connection.start(); + + // Wait for the messageConsumer to have received all the messages it needs + synchronized (_lock) + { + while (!_finished && !_failed) + { + _lock.wait(); + } + } + + // If the MessageListener abruptly failed (probably due to receiving a non-text message) + if (_failed) + { + System.out.println(CLASS + ": This sample failed as it received unexpected messages"); + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } + + /** + * This method is required by the MessageListener interface. It + * will be invoked when messages are available. + * After receiving the finish message (That's all, folks!) it releases a lock so that the + * main program may continue. + * + * @param message The message. + */ + public void onMessage(Message message) + { + try + { + String text; + if (message instanceof TextMessage) + { + text = ((TextMessage) message).getText(); + } + else + { + byte[] body = new byte[(int) ((BytesMessage) message).getBodyLength()]; + ((BytesMessage) message).readBytes(body); + text = new String(body); + } + if (text.equals("That's all, folks!")) + { + System.out.println(CLASS + ": Received final message " + text); + synchronized (_lock) + { + _finished = true; + _lock.notifyAll(); + } + } + else + { + System.out.println(CLASS + ": Received message: " + text); + } + } + catch (JMSException exp) + { + System.out.println(CLASS + ": Caught an exception handling a received message"); + exp.printStackTrace(); + synchronized (_lock) + { + _failed = true; + _lock.notifyAll(); + } + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Producer.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Producer.java new file mode 100755 index 0000000000..2e360f37bb --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/Producer.java @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.fanout; + +import java.util.Properties; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * Message producer example, sends message to a queue. + */ +public class Producer +{ + /* Used in log output. */ + private static final String CLASS = "Producer"; + + /* The queue name */ + private int numMessages = 10; + private short deliveryMode = DeliveryMode.NON_PERSISTENT; + + + /** + * Run the message producer example. + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Producer producer = new Producer(); + producer.runTest(); + } + + private void runTest() + { + try + { + + Properties properties = new Properties(); + properties.load(this.getClass().getResourceAsStream("fanout.properties")); + + //Create the initial context + Context ctx = new InitialContext(properties); + + Destination destination = (Destination)ctx.lookup("fanoutQueue"); + + // Declare the connection + ConnectionFactory conFac = (ConnectionFactory)ctx.lookup("qpidConnectionfactory"); + Connection connection = conFac.createConnection(); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // lookup the queue + //Queue destination = session.createQueue(_queueName); + + // Create a Message producer + System.out.println(CLASS + ": Creating a Message Producer"); + MessageProducer messageProducer = session.createProducer(destination); + + // Create a Message + TextMessage message; + System.out.println(CLASS + ": Creating a TestMessage to send to the destination"); + + // Loop to publish the requested number of messages. + for (int i = 1; i < numMessages + 1; i++) + { + // NOTE: We have NOT HAD TO START THE CONNECTION TO BEGIN SENDING messages, + // this is different to the consumer end as a CONSUMERS CONNECTIONS MUST BE STARTED BEFORE RECEIVING. + message = session.createTextMessage("Message " + i); + System.out.println(CLASS + ": Sending message: " + i); + messageProducer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + } + + // And send a final message to indicate termination. + message = session.createTextMessage("That's all, folks!"); + messageProducer.send(message, deliveryMode, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + + // Close the connection to the broker + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + exp.printStackTrace(); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/fanout.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/fanout.properties new file mode 100644 index 0000000000..901994541d --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/fanout.properties @@ -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. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# Register an AMQP destination in JNDI +# destination.[jniName] = [BindingURL] +destination.fanoutQueue1 = fanout://amq.fanout//message_queue1 +destination.fanoutQueue2 = fanout://amq.fanout//message_queue2 +destination.fanoutQueue3 = fanout://amq.fanout//message_queue3 + +# for producer +destination.fanoutQueue = fanout://amq.fanout//message_queue \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify new file mode 100644 index 0000000000..6617e37e85 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +cpp=$CPP/fanout + +fanout_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Listener $1 +} + +fanout_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Producer +} + +background "can receive messages" fanout_listener_java fanoutQueue1 +background "can receive messages" fanout_listener_java fanoutQueue2 +background "can receive messages" fanout_listener_java fanoutQueue3 +clients fanout_producer_java +outputs ./fanout_producer_java.out "./fanout_listener_java.out | remove_uuid" "./fanout_listener_javaX.out | remove_uuid" "./fanout_listener_javaXX.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify.in new file mode 100644 index 0000000000..c36a515c2a --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify.in @@ -0,0 +1,70 @@ +==== fanout_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== fanout_listener_java.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received message: Message 10 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received message: Message 10 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaXX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received message: Message 10 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java new file mode 100644 index 0000000000..de057ea3b1 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java @@ -0,0 +1,13 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +cpp=$CPP/fanout + +fanout_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Listener $1 +} + +background "can receive messages" fanout_listener_java fanoutQueue1 +background "can receive messages" fanout_listener_java fanoutQueue2 +background "can receive messages" fanout_listener_java fanoutQueue3 +clients $cpp/fanout_producer +outputs $cpp/fanout_producer.out "./fanout_listener_java.out | remove_uuid" "./fanout_listener_javaX.out | remove_uuid" "./fanout_listener_javaXX.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java.in new file mode 100644 index 0000000000..905fe3d0bc --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_cpp_java.in @@ -0,0 +1,55 @@ +==== fanout_producer.out +==== fanout_listener_java.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 0 +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 0 +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaXX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: Message 0 +Listener: Received message: Message 1 +Listener: Received message: Message 2 +Listener: Received message: Message 3 +Listener: Received message: Message 4 +Listener: Received message: Message 5 +Listener: Received message: Message 6 +Listener: Received message: Message 7 +Listener: Received message: Message 8 +Listener: Received message: Message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp new file mode 100644 index 0000000000..dab6114572 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp @@ -0,0 +1,13 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +cpp=$CPP/fanout + +fanout_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Producer +} + +background "Listening" $cpp/listener +background "Listening" $cpp/listener +background "Listening" $cpp/listener +clients fanout_producer_java +outputs ./fanout_producer_java.out "$cpp/listener.out | remove_uuid" "$cpp/listenerX.out | remove_uuid" "$cpp/listenerXX.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp.in new file mode 100644 index 0000000000..03e75e39c6 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_cpp.in @@ -0,0 +1,58 @@ +==== fanout_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== listener.out | remove_uuid +Listening +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: Message 10 +Message: That's all, folks! +Shutting down listener for +==== listenerX.out | remove_uuid +Listening +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: Message 10 +Message: That's all, folks! +Shutting down listener for +==== listenerXX.out | remove_uuid +Listening +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: Message 10 +Message: That's all, folks! +Shutting down listener for diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python new file mode 100644 index 0000000000..1641d88354 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python @@ -0,0 +1,13 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +py=$PYTHON_EXAMPLES/fanout + +fanout_producer_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Producer +} + +background "Subscribed" $py/fanout_consumer.py +background "Subscribed" $py/fanout_consumer.py +background "Subscribed" $py/fanout_consumer.py +clients fanout_producer_java +outputs ./fanout_producer_java.out "$py/fanout_consumer.py.out | remove_uuid" "$py/fanout_consumer.pyX.out | remove_uuid" "$py/fanout_consumer.pyXX.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python.in new file mode 100644 index 0000000000..0089e55c16 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_java_python.in @@ -0,0 +1,55 @@ +==== fanout_producer_java.out +Producer: Creating a non-transacted, auto-acknowledged session +Producer: Creating a Message Producer +Producer: Creating a TestMessage to send to the destination +Producer: Sending message: 1 +Producer: Sending message: 2 +Producer: Sending message: 3 +Producer: Sending message: 4 +Producer: Sending message: 5 +Producer: Sending message: 6 +Producer: Sending message: 7 +Producer: Sending message: 8 +Producer: Sending message: 9 +Producer: Sending message: 10 +Producer: Closing connection +Producer: Closing JNDI context +==== fanout_consumer.py.out | remove_uuid +Subscribed to queue +Message 1 +Message 2 +Message 3 +Message 4 +Message 5 +Message 6 +Message 7 +Message 8 +Message 9 +Message 10 +That's all, folks! +==== fanout_consumer.pyX.out | remove_uuid +Subscribed to queue +Message 1 +Message 2 +Message 3 +Message 4 +Message 5 +Message 6 +Message 7 +Message 8 +Message 9 +Message 10 +That's all, folks! +==== fanout_consumer.pyXX.out | remove_uuid +Subscribed to queue +Message 1 +Message 2 +Message 3 +Message 4 +Message 5 +Message 6 +Message 7 +Message 8 +Message 9 +Message 10 +That's all, folks! diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java new file mode 100644 index 0000000000..0f05663985 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java @@ -0,0 +1,13 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +# The JMS producer doesn't create qeueues so utilising the c++ declare_queues +py=$PYTHON_EXAMPLES/fanout + +fanout_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.fanout.Listener $1 +} + +background "can receive messages" fanout_listener_java fanoutQueue1 +background "can receive messages" fanout_listener_java fanoutQueue2 +background "can receive messages" fanout_listener_java fanoutQueue3 +clients $py/fanout_producer.py +outputs $py/fanout_producer.py.out "./fanout_listener_java.out | remove_uuid" "./fanout_listener_javaX.out | remove_uuid" "./fanout_listener_javaXX.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java.in new file mode 100644 index 0000000000..1d8e1c2790 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/fanout/verify_python_java.in @@ -0,0 +1,55 @@ +==== fanout_producer.py.out +==== fanout_listener_java.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: message 0 +Listener: Received message: message 1 +Listener: Received message: message 2 +Listener: Received message: message 3 +Listener: Received message: message 4 +Listener: Received message: message 5 +Listener: Received message: message 6 +Listener: Received message: message 7 +Listener: Received message: message 8 +Listener: Received message: message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: message 0 +Listener: Received message: message 1 +Listener: Received message: message 2 +Listener: Received message: message 3 +Listener: Received message: message 4 +Listener: Received message: message 5 +Listener: Received message: message 6 +Listener: Received message: message 7 +Listener: Received message: message 8 +Listener: Received message: message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context +==== fanout_listener_javaXX.out | remove_uuid +Listener: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Creating a MessageConsumer +Listener: Starting connection so MessageConsumer can receive messages +Listener: Received message: message 0 +Listener: Received message: message 1 +Listener: Received message: message 2 +Listener: Received message: message 3 +Listener: Received message: message 4 +Listener: Received message: message 5 +Listener: Received message: message 6 +Listener: Received message: message 7 +Listener: Received message: message 8 +Listener: Received message: message 9 +Listener: Received final message That's all, folks! +Listener: Closing connection +Listener: Closing JNDI context diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Listener.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Listener.java new file mode 100644 index 0000000000..1a3d40041d --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Listener.java @@ -0,0 +1,214 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.jmsexample.pubsub; + +import java.util.Properties; + +import javax.jms.BytesMessage; +import javax.jms.ConnectionFactory; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; +import javax.naming.Context; +import javax.naming.InitialContext; + +/** + * The example creates a TopicSubscriber on the specified + * Topic and uses a MessageListener with this TopicSubscriber + * in order to enable asynchronous delivery. + */ +public class Listener +{ + /* Used in log output. */ + private static final String CLASS="Listener"; + + /* An object to synchronize on. */ + private final static Object _lock=new Object(); + + /* A boolean to indicate a clean finish. */ + private static int _finished=0; + + /* A boolean to indicate an unsuccesful finish. */ + private static boolean _failed=false; + + /** + * Run the message consumer example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Listener listener=new Listener(); + listener.runTest(); + } + + private void createListener(Context ctx,TopicSession session,String topicName) throws Exception{ + // lookup the topic usa + Topic topic=(Topic) ctx.lookup(topicName); + // Create a Message Subscriber + System.out.println(CLASS + ": Creating a Message Subscriber for topic " + topicName); + javax.jms.TopicSubscriber messageSubscriber=session.createSubscriber(topic); + + // Set a message listener on the messageConsumer + messageSubscriber.setMessageListener(new MyMessageListener(topicName)); + + } + + /** + * Start the example. + */ + private void runTest() + { + try + { + Properties properties=new Properties(); + properties.load(this.getClass().getResourceAsStream("pubsub.properties")); + + //Create the initial context + Context ctx=new InitialContext(properties); + + // Declare the connection + ConnectionFactory conFac=(ConnectionFactory) ctx.lookup("qpidConnectionfactory"); + TopicConnection connection=(TopicConnection) conFac.createConnection(); + + // As this application is using a TopicSubscriber we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a TopicSubscriber"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses + // the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + TopicSession session=connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + + createListener(ctx,session,"usa"); + createListener(ctx,session,"europe"); + createListener(ctx,session,"news"); + createListener(ctx,session,"weather"); + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so TopicSubscriber can receive messages"); + connection.start(); + + // Wait for the messageConsumer to have received all the messages it needs + synchronized (_lock) + { + while (_finished < 4 && !_failed) + { + _lock.wait(); + } + } + + // If the MessageListener abruptly failed (probably due to receiving a non-text message) + if (_failed) + { + System.out.println(CLASS + ": This sample failed as it received unexpected messages"); + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + exp.printStackTrace(); + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } + + private class MyMessageListener implements MessageListener + { + /* The topic this subscriber is subscribing to */ + private String _topicName; + + public MyMessageListener(String topicName) + { + _topicName=topicName; + } + + /** + * This method is required by the MessageListener interface. It + * will be invoked when messages are available. + * After receiving the final message it releases a lock so that the + * main program may continue. + * + * @param message The message. + */ + public void onMessage(Message message) + { + try + { + String text; + if (message instanceof TextMessage) + { + text=((TextMessage) message).getText(); + } + else + { + byte[] body=new byte[(int) ((BytesMessage) message).getBodyLength()]; + ((BytesMessage) message).readBytes(body); + text=new String(body); + } + if (text.equals("That's all, folks!")) + { + System.out.println(CLASS + ": Shutting down listener for " + _topicName); + synchronized (_lock) + { + _finished++; + _lock.notifyAll(); + } + } + else + { + System.out.println(CLASS + ": Received message for topic: " + _topicName + ": " + text); + } + } + catch (JMSException exp) + { + System.out.println(CLASS + ": Caught an exception handling a received message"); + exp.printStackTrace(); + synchronized (_lock) + { + _failed=true; + _lock.notifyAll(); + } + } + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Publisher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Publisher.java new file mode 100644 index 0000000000..360b2c6aed --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/Publisher.java @@ -0,0 +1,133 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.pubsub; + + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import java.util.Properties; + +/** + * Publish messages to topics + */ +public class Publisher +{ + /* Used in log output. */ + private static final String CLASS="Publisher"; + + /** + * Run the message producer example. + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Publisher publisher = new Publisher(); + publisher.runTest(); + } + + private void runTest() + { + try + { + Properties properties=new Properties(); + properties.load(this.getClass().getResourceAsStream("pubsub.properties")); + + //Create the initial context + Context ctx=new InitialContext(properties); + + // Declare the connection + ConnectionFactory conFac=(ConnectionFactory) ctx.lookup("qpidConnectionfactory"); + TopicConnection connection= (TopicConnection) conFac.createConnection(); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + TopicSession session=connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + + // Create a Message + TextMessage message; + System.out.println(CLASS + ": Creating a TestMessage to send to the topics"); + message=session.createTextMessage(); + + // lookup the topics usa.weather + Topic topic = (Topic)ctx.lookup("usa.weather"); + // Create a Message Publisher + System.out.println(CLASS + ": Creating a Message Publisher for topic usa.weather"); + TopicPublisher messagePublisher=session.createPublisher(topic); + publishMessages(message, messagePublisher); + + // lookup the topics usa.news + topic = (Topic)ctx.lookup("usa.news"); + // Create a Message Publisher + System.out.println(CLASS + ": Creating a Message Publisher for topic usa.news"); + messagePublisher=session.createPublisher(topic); + publishMessages(message, messagePublisher); + + // lookup the topics europe.weather + topic = (Topic)ctx.lookup("europe.weather"); + // Create a Message Publisher + System.out.println(CLASS + ": Creating a Message Publisher for topic europe.weather"); + messagePublisher=session.createPublisher(topic); + publishMessages(message, messagePublisher); + + // lookup the topics europe.news + topic = (Topic)ctx.lookup("europe.news"); + // Create a Message Publisher + System.out.println(CLASS + ": Creating a Message Publisher for topic europe.news"); + messagePublisher = session.createPublisher(topic); + publishMessages(message, messagePublisher); + + // send the final message + message=session.createTextMessage("That's all, folks!"); + topic = (Topic)ctx.lookup("control"); + // Create a Message Publisher + messagePublisher = session.createPublisher(topic); + messagePublisher + .send(message, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + + + // Close the connection to the broker + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } + + private void publishMessages(TextMessage message, TopicPublisher messagePublisher) throws JMSException + { + // Loop to publish 5 messages. + for (int i=1; i <= 6; i++) + { + message.setText("message " + i); + System.out.println(CLASS + ": Sending " + message.getText()); + messagePublisher + .send(message, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/pubsub.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/pubsub.properties new file mode 100644 index 0000000000..91c3de721b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/pubsub.properties @@ -0,0 +1,36 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.usa.weather = usa.weather,control +topic.usa.news = usa.news,control +topic.europe.weather = europe.weather,control +topic.europe.news = europe.news,control +topic.weather = #.weather,control +topic.news = #.news,control +topic.europe = europe.#,control +topic.usa = usa.#,control +topic.control = control \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify new file mode 100644 index 0000000000..588a086752 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify @@ -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. +# + +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/pub-sub + +topic_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Listener +} + +topic_publisher_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Publisher +} + +background "can receive messages" topic_listener_java +clients topic_publisher_java +outputs ./topic_publisher_java.out "topic_listener_java.out | remove_uuid | sort" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify.in new file mode 100644 index 0000000000..6e3074e611 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify.in @@ -0,0 +1,95 @@ +==== topic_publisher_java.out +Publisher: Creating a non-transacted, auto-acknowledged session +Publisher: Creating a TestMessage to send to the topics +Publisher: Creating a Message Publisher for topic usa.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic usa.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Closing connection +Publisher: Closing JNDI context +==== topic_listener_java.out | remove_uuid | sort +Listener: Closing connection +Listener: Closing JNDI context +Listener: Creating a Message Subscriber for topic europe +Listener: Creating a Message Subscriber for topic news +Listener: Creating a Message Subscriber for topic usa +Listener: Creating a Message Subscriber for topic weather +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Received message for topic: europe: message 1 +Listener: Received message for topic: europe: message 1 +Listener: Received message for topic: europe: message 2 +Listener: Received message for topic: europe: message 2 +Listener: Received message for topic: europe: message 3 +Listener: Received message for topic: europe: message 3 +Listener: Received message for topic: europe: message 4 +Listener: Received message for topic: europe: message 4 +Listener: Received message for topic: europe: message 5 +Listener: Received message for topic: europe: message 5 +Listener: Received message for topic: europe: message 6 +Listener: Received message for topic: europe: message 6 +Listener: Received message for topic: news: message 1 +Listener: Received message for topic: news: message 1 +Listener: Received message for topic: news: message 2 +Listener: Received message for topic: news: message 2 +Listener: Received message for topic: news: message 3 +Listener: Received message for topic: news: message 3 +Listener: Received message for topic: news: message 4 +Listener: Received message for topic: news: message 4 +Listener: Received message for topic: news: message 5 +Listener: Received message for topic: news: message 5 +Listener: Received message for topic: news: message 6 +Listener: Received message for topic: news: message 6 +Listener: Received message for topic: usa: message 1 +Listener: Received message for topic: usa: message 1 +Listener: Received message for topic: usa: message 2 +Listener: Received message for topic: usa: message 2 +Listener: Received message for topic: usa: message 3 +Listener: Received message for topic: usa: message 3 +Listener: Received message for topic: usa: message 4 +Listener: Received message for topic: usa: message 4 +Listener: Received message for topic: usa: message 5 +Listener: Received message for topic: usa: message 5 +Listener: Received message for topic: usa: message 6 +Listener: Received message for topic: usa: message 6 +Listener: Received message for topic: weather: message 1 +Listener: Received message for topic: weather: message 1 +Listener: Received message for topic: weather: message 2 +Listener: Received message for topic: weather: message 2 +Listener: Received message for topic: weather: message 3 +Listener: Received message for topic: weather: message 3 +Listener: Received message for topic: weather: message 4 +Listener: Received message for topic: weather: message 4 +Listener: Received message for topic: weather: message 5 +Listener: Received message for topic: weather: message 5 +Listener: Received message for topic: weather: message 6 +Listener: Received message for topic: weather: message 6 +Listener: Setting an ExceptionListener on the connection as sample uses a TopicSubscriber +Listener: Shutting down listener for europe +Listener: Shutting down listener for news +Listener: Shutting down listener for usa +Listener: Shutting down listener for weather +Listener: Starting connection so TopicSubscriber can receive messages diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java new file mode 100644 index 0000000000..9276b3e21b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/pub-sub + +topic_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Listener +} + +background "can receive messages" topic_listener_java +clients $cpp/topic_publisher +outputs $cpp/topic_publisher.out "topic_listener_java.out | remove_uuid | sort" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java.in new file mode 100644 index 0000000000..62cc76baec --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_cpp_java.in @@ -0,0 +1,55 @@ +==== topic_publisher.out +==== topic_listener_java.out | remove_uuid | sort +Listener: Closing connection +Listener: Closing JNDI context +Listener: Creating a Message Subscriber for topic europe +Listener: Creating a Message Subscriber for topic news +Listener: Creating a Message Subscriber for topic usa +Listener: Creating a Message Subscriber for topic weather +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Received message for topic: europe: Message 0 +Listener: Received message for topic: europe: Message 0 +Listener: Received message for topic: europe: Message 1 +Listener: Received message for topic: europe: Message 1 +Listener: Received message for topic: europe: Message 2 +Listener: Received message for topic: europe: Message 2 +Listener: Received message for topic: europe: Message 3 +Listener: Received message for topic: europe: Message 3 +Listener: Received message for topic: europe: Message 4 +Listener: Received message for topic: europe: Message 4 +Listener: Received message for topic: news: Message 0 +Listener: Received message for topic: news: Message 0 +Listener: Received message for topic: news: Message 1 +Listener: Received message for topic: news: Message 1 +Listener: Received message for topic: news: Message 2 +Listener: Received message for topic: news: Message 2 +Listener: Received message for topic: news: Message 3 +Listener: Received message for topic: news: Message 3 +Listener: Received message for topic: news: Message 4 +Listener: Received message for topic: news: Message 4 +Listener: Received message for topic: usa: Message 0 +Listener: Received message for topic: usa: Message 0 +Listener: Received message for topic: usa: Message 1 +Listener: Received message for topic: usa: Message 1 +Listener: Received message for topic: usa: Message 2 +Listener: Received message for topic: usa: Message 2 +Listener: Received message for topic: usa: Message 3 +Listener: Received message for topic: usa: Message 3 +Listener: Received message for topic: usa: Message 4 +Listener: Received message for topic: usa: Message 4 +Listener: Received message for topic: weather: Message 0 +Listener: Received message for topic: weather: Message 0 +Listener: Received message for topic: weather: Message 1 +Listener: Received message for topic: weather: Message 1 +Listener: Received message for topic: weather: Message 2 +Listener: Received message for topic: weather: Message 2 +Listener: Received message for topic: weather: Message 3 +Listener: Received message for topic: weather: Message 3 +Listener: Received message for topic: weather: Message 4 +Listener: Received message for topic: weather: Message 4 +Listener: Setting an ExceptionListener on the connection as sample uses a TopicSubscriber +Listener: Shutting down listener for europe +Listener: Shutting down listener for news +Listener: Shutting down listener for usa +Listener: Shutting down listener for weather +Listener: Starting connection so TopicSubscriber can receive messages diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp new file mode 100644 index 0000000000..af22b3b82c --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/pub-sub + +topic_publisher_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Publisher +} + +background "Listening" $cpp/topic_listener +clients topic_publisher_java +outputs ./topic_publisher_java.out "$cpp/topic_listener.out | remove_uuid | sort" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp.in new file mode 100644 index 0000000000..8c5c26eaca --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_cpp.in @@ -0,0 +1,99 @@ +==== topic_publisher_java.out +Publisher: Creating a non-transacted, auto-acknowledged session +Publisher: Creating a TestMessage to send to the topics +Publisher: Creating a Message Publisher for topic usa.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic usa.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Closing connection +Publisher: Closing JNDI context +==== topic_listener.out | remove_uuid | sort +Declaring queue: europe +Declaring queue: news +Declaring queue: usa +Declaring queue: weather +Listening for messages ... +Message: message 1 from europe +Message: message 1 from europe +Message: message 1 from news +Message: message 1 from news +Message: message 1 from usa +Message: message 1 from usa +Message: message 1 from weather +Message: message 1 from weather +Message: message 2 from europe +Message: message 2 from europe +Message: message 2 from news +Message: message 2 from news +Message: message 2 from usa +Message: message 2 from usa +Message: message 2 from weather +Message: message 2 from weather +Message: message 3 from europe +Message: message 3 from europe +Message: message 3 from news +Message: message 3 from news +Message: message 3 from usa +Message: message 3 from usa +Message: message 3 from weather +Message: message 3 from weather +Message: message 4 from europe +Message: message 4 from europe +Message: message 4 from news +Message: message 4 from news +Message: message 4 from usa +Message: message 4 from usa +Message: message 4 from weather +Message: message 4 from weather +Message: message 5 from europe +Message: message 5 from europe +Message: message 5 from news +Message: message 5 from news +Message: message 5 from usa +Message: message 5 from usa +Message: message 5 from weather +Message: message 5 from weather +Message: message 6 from europe +Message: message 6 from europe +Message: message 6 from news +Message: message 6 from news +Message: message 6 from usa +Message: message 6 from usa +Message: message 6 from weather +Message: message 6 from weather +Message: That's all, folks! from europe +Message: That's all, folks! from news +Message: That's all, folks! from usa +Message: That's all, folks! from weather +Shutting down listener for europe +Shutting down listener for news +Shutting down listener for usa +Shutting down listener for weather +Subscribing to queue europe +Subscribing to queue news +Subscribing to queue usa +Subscribing to queue weather diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python new file mode 100644 index 0000000000..3758e0f014 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/pubsub + +topic_publisher_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Publisher +} + +background "Queues created" $py/topic_subscriber.py +clients topic_publisher_java +outputs ./topic_publisher_java.out "$py/topic_subscriber.py.out | remove_uuid | sort" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python.in new file mode 100644 index 0000000000..92184201d0 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_java_python.in @@ -0,0 +1,95 @@ +==== topic_publisher_java.out +Publisher: Creating a non-transacted, auto-acknowledged session +Publisher: Creating a TestMessage to send to the topics +Publisher: Creating a Message Publisher for topic usa.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic usa.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.weather +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Creating a Message Publisher for topic europe.news +Publisher: Sending message 1 +Publisher: Sending message 2 +Publisher: Sending message 3 +Publisher: Sending message 4 +Publisher: Sending message 5 +Publisher: Sending message 6 +Publisher: Closing connection +Publisher: Closing JNDI context +==== topic_subscriber.py.out | remove_uuid | sort +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 1 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 2 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 3 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +message 4 +message 5 +message 5 +message 5 +message 5 +message 5 +message 5 +message 5 +message 5 +message 6 +message 6 +message 6 +message 6 +message 6 +message 6 +message 6 +message 6 +Messages on 'europe' queue: +Messages on 'news' queue: +Messages on 'usa' queue: +Messages on 'weather' queue: +Queues created - please start the topic producer +Subscribing local queue 'local_europe' to europe-' +Subscribing local queue 'local_news' to news-' +Subscribing local queue 'local_usa' to usa-' +Subscribing local queue 'local_weather' to weather-' +That's all, folks! +That's all, folks! +That's all, folks! +That's all, folks! diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java new file mode 100644 index 0000000000..c2b516f376 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java @@ -0,0 +1,10 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/pubsub + +topic_listener_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.pubsub.Listener +} + +background "can receive messages" topic_listener_java +clients $py/topic_publisher.py +outputs $py/topic_publisher.py.out "topic_listener_java.out | remove_uuid | sort" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java.in new file mode 100644 index 0000000000..68b96cba2b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/pubsub/verify_python_java.in @@ -0,0 +1,55 @@ +==== topic_publisher.py.out +==== topic_listener_java.out | remove_uuid | sort +Listener: Closing connection +Listener: Closing JNDI context +Listener: Creating a Message Subscriber for topic europe +Listener: Creating a Message Subscriber for topic news +Listener: Creating a Message Subscriber for topic usa +Listener: Creating a Message Subscriber for topic weather +Listener: Creating a non-transacted, auto-acknowledged session +Listener: Received message for topic: europe: europe.news 0 +Listener: Received message for topic: europe: europe.news 1 +Listener: Received message for topic: europe: europe.news 2 +Listener: Received message for topic: europe: europe.news 3 +Listener: Received message for topic: europe: europe.news 4 +Listener: Received message for topic: europe: europe.weather 0 +Listener: Received message for topic: europe: europe.weather 1 +Listener: Received message for topic: europe: europe.weather 2 +Listener: Received message for topic: europe: europe.weather 3 +Listener: Received message for topic: europe: europe.weather 4 +Listener: Received message for topic: news: europe.news 0 +Listener: Received message for topic: news: europe.news 1 +Listener: Received message for topic: news: europe.news 2 +Listener: Received message for topic: news: europe.news 3 +Listener: Received message for topic: news: europe.news 4 +Listener: Received message for topic: news: usa.news 0 +Listener: Received message for topic: news: usa.news 1 +Listener: Received message for topic: news: usa.news 2 +Listener: Received message for topic: news: usa.news 3 +Listener: Received message for topic: news: usa.news 4 +Listener: Received message for topic: usa: usa.news 0 +Listener: Received message for topic: usa: usa.news 1 +Listener: Received message for topic: usa: usa.news 2 +Listener: Received message for topic: usa: usa.news 3 +Listener: Received message for topic: usa: usa.news 4 +Listener: Received message for topic: usa: usa.weather 0 +Listener: Received message for topic: usa: usa.weather 1 +Listener: Received message for topic: usa: usa.weather 2 +Listener: Received message for topic: usa: usa.weather 3 +Listener: Received message for topic: usa: usa.weather 4 +Listener: Received message for topic: weather: europe.weather 0 +Listener: Received message for topic: weather: europe.weather 1 +Listener: Received message for topic: weather: europe.weather 2 +Listener: Received message for topic: weather: europe.weather 3 +Listener: Received message for topic: weather: europe.weather 4 +Listener: Received message for topic: weather: usa.weather 0 +Listener: Received message for topic: weather: usa.weather 1 +Listener: Received message for topic: weather: usa.weather 2 +Listener: Received message for topic: weather: usa.weather 3 +Listener: Received message for topic: weather: usa.weather 4 +Listener: Setting an ExceptionListener on the connection as sample uses a TopicSubscriber +Listener: Shutting down listener for europe +Listener: Shutting down listener for news +Listener: Shutting down listener for usa +Listener: Shutting down listener for weather +Listener: Starting connection so TopicSubscriber can receive messages diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Client.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Client.java new file mode 100644 index 0000000000..0589a3801b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Client.java @@ -0,0 +1,153 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.requestResponse; + + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import java.util.Properties; + +/** + * This example illustrates the use of the JMS utility class QueueRequestor + * which provides a synchronous RPC-like abstraction using temporary destinations + * to deliver responses back to the client. + */ +public class Client +{ + /* Used in log output. */ + private static final String CLASS="Client"; + + + /** + * Run the message requestor example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Client requestor=new Client(); + requestor.runTest(); + } + + /** + * Start the example. + */ + private void runTest() + { + try + { + // Load JNDI properties + Properties properties=new Properties(); + properties.load(this.getClass().getResourceAsStream("requestResponse.properties")); + + //Create the initial context + Context ctx=new InitialContext(properties); + + // Lookup the connection factory + ConnectionFactory conFac = (ConnectionFactory) ctx.lookup("qpidConnectionfactory"); + + // create the connection + QueueConnection connection = (QueueConnection) conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + // Lookup the destination + Queue destination = (Queue) ctx.lookup("requestQueue"); + + // Create a QueueRequestor + System.out.println(CLASS + ": Creating a QueueRequestor"); + + QueueRequestor requestor = new QueueRequestor(session, destination); + + // Now start the connection + System.out.println(CLASS + ": Starting connection"); + connection.start(); + + // Create a message to send as a request for service + TextMessage request; + + // Send some messages to the server's request queue + String[] messages = {"Twas brillig, and the slithy toves", + "Did gire and gymble in the wabe.", + "All mimsy were the borogroves,", + "And the mome raths outgrabe."}; + + // Get the number of times that this sample should request service + for (String message : messages) + { + request = session.createTextMessage(message); + sendReceive(request, requestor); + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } + + private void sendReceive(TextMessage request, QueueRequestor requestor) throws JMSException + { + Message response; + response=requestor.request(request); + System.out.println(CLASS + ": \tRequest Content= " + request.getText()); + // Print out the details of the response received + String text; + if (response instanceof TextMessage) + { + text=((TextMessage) response).getText(); + } + else + { + byte[] body=new byte[(int) ((BytesMessage) response).getBodyLength()]; + ((BytesMessage) response).readBytes(body); + text=new String(body); + } + System.out.println(CLASS + ": \tResponse Content= " + text); + } +} + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Server.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Server.java new file mode 100644 index 0000000000..2ac349a879 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/Server.java @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.requestResponse; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import java.util.Properties; + +/** + * The example creates a MessageConsumer on the specified + * Destination which is used to synchronously consume messages. If a + * received message has a ReplyTo header then a new response message is sent + * to that specified destination. + */ +public class Server +{ + /* Used in log output. */ + private static final String CLASS="Server"; + + + /** + * Run the message mirror example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + Server server=new Server(); + server.runTest(); + } + + /** + * Start the example. + */ + private void runTest() + { + try + { + // Load JNDI properties + Properties properties=new Properties(); + properties.load(this.getClass().getResourceAsStream("requestResponse.properties")); + + //Create the initial context + Context ctx=new InitialContext(properties); + + // Lookup the connection factory + ConnectionFactory conFac=(ConnectionFactory) ctx.lookup("qpidConnectionfactory"); + + // create the connection + Connection connection=conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Create a session on the connection + // This session is a default choice of non-transacted and uses + // the auto acknowledge feature of a session. + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + + Session session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Lookup the destination + Queue destination = (Queue) ctx.lookup("requestQueue"); + + + // Create a MessageConsumer + System.out.println(CLASS + ": Creating a MessageConsumer"); + MessageConsumer messageConsumer=session.createConsumer(destination); + + /** + * Create a MessageProducer + */ + System.out.println(CLASS + ": Creating a MessageProducer"); + MessageProducer messageProducer; + + // Now the messageConsumer is set up we can start the connection + System.out.println(CLASS + ": Starting connection so MessageConsumer can receive messages"); + connection.start(); + + // Cycle round until all the messages are consumed. + Message requestMessage; + TextMessage responseMessage; + boolean end=false; + while (!end) + { + System.out.println(CLASS + ": Receiving the message"); + + requestMessage=messageConsumer.receive(); + + String text; + if (requestMessage instanceof TextMessage) + { + text=((TextMessage) requestMessage).getText(); + } + else + { + byte[] body=new byte[(int) ((BytesMessage) requestMessage).getBodyLength()]; + ((BytesMessage) requestMessage).readBytes(body); + text=new String(body); + } + + // Now bounce the message if a ReplyTo header was set. + if (requestMessage.getJMSReplyTo() != null) + { + System.out.println(CLASS + ": Activating response queue listener"); + responseMessage=session.createTextMessage(); + + responseMessage.setText(text.toUpperCase()); + System.out.println(CLASS + ": \tResponse = " + responseMessage.getText()); + + messageProducer=session.createProducer(requestMessage.getJMSReplyTo()); + messageProducer.send(responseMessage); + } + System.out.println(); + } + + // Close the connection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + exp.printStackTrace(); + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/requestResponse.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/requestResponse.properties new file mode 100644 index 0000000000..c467a4f123 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/requestResponse.properties @@ -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. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.requestQueue = request diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify new file mode 100644 index 0000000000..79f22aa88a --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify @@ -0,0 +1,34 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/pub-sub + +client_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Client +} + +server_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Server +} + +background "can receive messages" server_java +clients client_java +kill %% +outputs "client_java.out | remove_uuid" "server_java.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify.in new file mode 100644 index 0000000000..f2c244dea6 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify.in @@ -0,0 +1,38 @@ +==== client_java.out | remove_uuid +Client: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Client: Creating a non-transacted, auto-acknowledged session +Client: Creating a QueueRequestor +Client: Starting connection +Client: Request Content= Twas brillig, and the slithy toves +Client: Response Content= TWAS BRILLIG, AND THE SLITHY TOVES +Client: Request Content= Did gire and gymble in the wabe. +Client: Response Content= DID GIRE AND GYMBLE IN THE WABE. +Client: Request Content= All mimsy were the borogroves, +Client: Response Content= ALL MIMSY WERE THE BOROGROVES, +Client: Request Content= And the mome raths outgrabe. +Client: Response Content= AND THE MOME RATHS OUTGRABE. +Client: Closing connection +Client: Closing JNDI context +==== server_java.out | remove_uuid +Server: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Server: Creating a non-transacted, auto-acknowledged session +Server: Creating a MessageConsumer +Server: Creating a MessageProducer +Server: Starting connection so MessageConsumer can receive messages +Server: Receiving the message +Server: Activating response queue listener +Server: Response = TWAS BRILLIG, AND THE SLITHY TOVES + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = DID GIRE AND GYMBLE IN THE WABE. + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = ALL MIMSY WERE THE BOROGROVES, + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = AND THE MOME RATHS OUTGRABE. + +Server: Receiving the message diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java new file mode 100644 index 0000000000..6ef1b3b7e3 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java @@ -0,0 +1,12 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/request-response + +client_java() +{ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Client +} + +background "Waiting" $cpp/server +clients client_java +kill %% +outputs "client_java.out | remove_uuid" "$cpp/server.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java.in new file mode 100644 index 0000000000..4b7e7e0741 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_cpp_java.in @@ -0,0 +1,22 @@ +==== client_java.out | remove_uuid +Client: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Client: Creating a non-transacted, auto-acknowledged session +Client: Creating a QueueRequestor +Client: Starting connection +Client: Request Content= Twas brillig, and the slithy toves +Client: Response Content= TWAS BRILLIG, AND THE SLITHY TOVES +Client: Request Content= Did gire and gymble in the wabe. +Client: Response Content= DID GIRE AND GYMBLE IN THE WABE. +Client: Request Content= All mimsy were the borogroves, +Client: Response Content= ALL MIMSY WERE THE BOROGROVES, +Client: Request Content= And the mome raths outgrabe. +Client: Response Content= AND THE MOME RATHS OUTGRABE. +Client: Closing connection +Client: Closing JNDI context +==== server.out | remove_uuid +Activating request queue listener for: request +Waiting for requests +Request: Twas brillig, and the slithy toves (TempQueue) +Request: Did gire and gymble in the wabe. (TempQueue) +Request: All mimsy were the borogroves, (TempQueue) +Request: And the mome raths outgrabe. (TempQueue) diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp new file mode 100644 index 0000000000..a1c5aa325d --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp @@ -0,0 +1,12 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +cpp=$CPP/request-response + +server_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Server +} + +background "can receive messages" server_java +clients $cpp/client +#ps -ao pid,cmd | awk '/qpid-client-.jar/{ print $1 }' | xargs -r kill +kill %% +outputs "$cpp/client.out | remove_uuid" "server_java.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp.in new file mode 100644 index 0000000000..9cccf39393 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_cpp.in @@ -0,0 +1,35 @@ +==== client.out | remove_uuid +Activating response queue listener for: client +Request: Twas brillig, and the slithy toves +Request: Did gire and gymble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Waiting for all responses to arrive ... +Response: TWAS BRILLIG, AND THE SLITHY TOVES +Response: DID GIRE AND GYMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +Shutting down listener for client +==== server_java.out | remove_uuid +Server: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Server: Creating a non-transacted, auto-acknowledged session +Server: Creating a MessageConsumer +Server: Creating a MessageProducer +Server: Starting connection so MessageConsumer can receive messages +Server: Receiving the message +Server: Activating response queue listener +Server: Response = TWAS BRILLIG, AND THE SLITHY TOVES + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = DID GIRE AND GYMBLE IN THE WABE. + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = ALL MIMSY WERE THE BOROGROVES, + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = AND THE MOME RATHS OUTGRABE. + +Server: Receiving the message diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python new file mode 100644 index 0000000000..0760952527 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python @@ -0,0 +1,11 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/request-response + +server_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Server +} + +background "can receive messages" server_java +clients $py/client.py +kill %% +outputs "$py/client.py.out | remove_uuid" "server_java.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python.in new file mode 100644 index 0000000000..bffe9d2842 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_java_python.in @@ -0,0 +1,34 @@ +==== client.py.out | remove_uuid +Request: Twas brillig, and the slithy toves +Request: Did gyre and gimble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Messages on queue: reply_to: +Response: TWAS BRILLIG, AND THE SLITHY TOVES +Response: DID GYRE AND GIMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +No more messages! +==== server_java.out | remove_uuid +Server: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Server: Creating a non-transacted, auto-acknowledged session +Server: Creating a MessageConsumer +Server: Creating a MessageProducer +Server: Starting connection so MessageConsumer can receive messages +Server: Receiving the message +Server: Activating response queue listener +Server: Response = TWAS BRILLIG, AND THE SLITHY TOVES + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = DID GYRE AND GIMBLE IN THE WABE. + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = ALL MIMSY WERE THE BOROGROVES, + +Server: Receiving the message +Server: Activating response queue listener +Server: Response = AND THE MOME RATHS OUTGRABE. + +Server: Receiving the message diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java new file mode 100644 index 0000000000..6ea526e914 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java @@ -0,0 +1,11 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/request-response + +client_java(){ +java -Dlog4j.configuration=file://"$JAVA"/log4j.xml -cp "$CLASSPATH" org.apache.qpid.example.jmsexample.requestResponse.Client +} + +background "Request server running" $py/server.py +clients client_java +kill %% +outputs "client_java.out | remove_uuid" "$py/server.py.out | remove_uuid" diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java.in b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java.in new file mode 100644 index 0000000000..6e53ca3281 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/requestResponse/verify_python_java.in @@ -0,0 +1,18 @@ +==== client_java.out | remove_uuid +Client: Setting an ExceptionListener on the connection as sample uses a MessageConsumer +Client: Creating a non-transacted, auto-acknowledged session +Client: Creating a QueueRequestor +Client: Starting connection +Client: Request Content= Twas brillig, and the slithy toves +Client: Response Content= TWAS BRILLIG, AND THE SLITHY TOVES +Client: Request Content= Did gire and gymble in the wabe. +Client: Response Content= DID GIRE AND GYMBLE IN THE WABE. +Client: Request Content= All mimsy were the borogroves, +Client: Response Content= ALL MIMSY WERE THE BOROGROVES, +Client: Request Content= And the mome raths outgrabe. +Client: Response Content= AND THE MOME RATHS OUTGRABE. +Client: Closing connection +Client: Closing JNDI context +==== server.py.out | remove_uuid +Request server running - run your client now. +(Times out after 100 seconds ...) diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/QueueToTopic.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/QueueToTopic.java new file mode 100644 index 0000000000..f3bf9f8686 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/QueueToTopic.java @@ -0,0 +1,259 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.jmsexample.transacted; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import java.util.Properties; + +/** + * Transactional message example sends a number of messages to a Queue + * and then uses a transacted session to move them from the Queue to a Topic. + *

+ *

The program completes the following steps: + *

    + *
  • Publish the specified number of messages to the queue.
  • + *
  • Within a transacted session consume all messages from the queue + * and publish them to the topic.
  • + *
  • By default commit the transacted session, unless the "-rollback true" + * option is specified in which case roll it back.
  • + *
  • Check for outstanding messages on the queue.
  • + *
  • Check for outstanding messages on the topic.
  • + *
+ *

+ */ +public class QueueToTopic +{ + /* Used in log output. */ + private static final String CLASS="QueueToTopic"; + + + /* Specify if the transaction is committed */ + private boolean _commit; + + /** + * Create a QueueToTopic client. + * + * @param commit Specifies if the transaction should be committed. + */ + public QueueToTopic(boolean commit) + { + _commit=commit; + } + + /** + * Run the message mover example. + * + * @param args Command line arguments. + */ + public static void main(String[] args) + { + boolean commit=true; + if (args.length > 1) + { + if (args[0].equalsIgnoreCase("-rollback")) + { + commit=!Boolean.getBoolean(args[1]); + } + } + QueueToTopic mover=new QueueToTopic(commit); + mover.runTest(); + } + + private void runTest() + { + try + { + // Load JNDI properties + Properties properties=new Properties(); + properties.load(this.getClass().getResourceAsStream("transacted.properties")); + + //Create the initial context + Context ctx=new InitialContext(properties); + + // Lookup the connection factory + ConnectionFactory conFac=(ConnectionFactory) ctx.lookup("qpidConnectionfactory"); + // create the connection + Connection connection=conFac.createConnection(); + + // As this application is using a MessageConsumer we need to set an ExceptionListener on the connection + // so that errors raised within the JMS client library can be reported to the application + System.out.println( + CLASS + ": Setting an ExceptionListener on the connection as sample uses a MessageConsumer"); + + connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmse) + { + // The connection may have broken invoke reconnect code if available. + System.err.println(CLASS + ": The sample received an exception through the ExceptionListener"); + System.exit(0); + } + }); + + // Start the connection + connection.start(); + + /** + * Create nonTransactedSession. This non-transacted auto-ack session is used to create the MessageProducer + * that is used to populate the queue and the MessageConsumer that is used to consume the messages + * from the topic. + */ + System.out.println(CLASS + ": Creating a non-transacted, auto-acknowledged session"); + Session nonTransactedSession=connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Lookup the queue + Queue queue=(Queue) ctx.lookup("transactedQueue"); + + // Lookup the topic + Topic topic=(Topic) ctx.lookup("transactedTopic"); + + // Make sure that the queue is empty + System.out.print(CLASS + ": Purging messages from queue..."); + MessageConsumer queueMessageConsumer=nonTransactedSession.createConsumer(queue); + Message purgedMessage; + int numberPurged=-1; + do + { + purgedMessage=queueMessageConsumer.receiveNoWait(); + numberPurged++; + } + while (purgedMessage != null); + System.out.println(numberPurged + " message(s) purged."); + + // Create the MessageProducer for the queue + System.out.println(CLASS + ": Creating a MessageProducer for the queue"); + MessageProducer messageProducer=nonTransactedSession.createProducer(queue); + + // Now create the MessageConsumer for the topic + System.out.println(CLASS + ": Creating a MessageConsumer for the topic"); + MessageConsumer topicMessageConsumer=nonTransactedSession.createConsumer(topic); + + // Create a textMessage. We're using a TextMessage for this example. + System.out.println(CLASS + ": Creating a TestMessage to send to the destination"); + TextMessage textMessage=nonTransactedSession.createTextMessage("Sample text message"); + + // Loop to publish the requested number of messages to the queue. + for (int i=1; i <= 5; i++) + { + messageProducer + .send(textMessage, DeliveryMode.PERSISTENT, Message.DEFAULT_PRIORITY, + Message.DEFAULT_TIME_TO_LIVE); + + // Print out details of textMessage just sent + System.out.println(CLASS + ": Message sent: " + i + " " + textMessage.getJMSMessageID()); + } + + // Create a new transacted Session to move the messages from the queue to the topic + Session transactedSession=connection.createSession(true, Session.SESSION_TRANSACTED); + + // Create a new message consumer from the queue + MessageConsumer transactedConsumer=transactedSession.createConsumer(queue); + + // Create a new message producer for the topic + MessageProducer transactedProducer=transactedSession.createProducer(topic); + + // Loop to consume the messages from the queue and publish them to the topic + Message receivedMessage; + for (int i=1; i <= 5; i++) + { + // Receive a message + receivedMessage=transactedConsumer.receive(); + System.out.println(CLASS + ": Moving message: " + i + " " + receivedMessage.getJMSMessageID()); + // Publish it to the topic + transactedProducer.send(receivedMessage); + } + + // Either commit or rollback the transacted session based on the command line args. + if (_commit) + { + System.out.println(CLASS + ": Committing transacted session."); + transactedSession.commit(); + } + else + { + System.out.println(CLASS + ": Rolling back transacted session."); + transactedSession.rollback(); + } + + // Now consume any outstanding messages on the queue + System.out.print(CLASS + ": Mopping up messages from queue"); + if (_commit) + { + System.out.print(" (expecting none)..."); + } + else + { + System.out.print(" (expecting " + 5 + ")..."); + } + + Message moppedMessage; + int numberMopped=0; + do + { + moppedMessage=queueMessageConsumer.receiveNoWait(); + if (moppedMessage != null) + { + numberMopped++; + } + } + while (moppedMessage != null); + System.out.println(numberMopped + " message(s) mopped."); + + // Now consume any outstanding messages for the topic subscriber + System.out.print(CLASS + ": Mopping up messages from topic"); + + if (_commit) + { + System.out.print(" (expecting " + 5 + ")..."); + } + else + { + System.out.print(" (expecting none)..."); + } + + numberMopped=0; + do + { + moppedMessage=topicMessageConsumer.receiveNoWait(); + if (moppedMessage != null) + { + numberMopped++; + } + } + while (moppedMessage != null); + System.out.println(numberMopped + " message(s) mopped."); + + // Close the QueueConnection to the server + System.out.println(CLASS + ": Closing connection"); + connection.close(); + + // Close the JNDI reference + System.out.println(CLASS + ": Closing JNDI context"); + ctx.close(); + } + catch (Exception exp) + { + System.err.println(CLASS + ": Caught an Exception: " + exp); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/transacted.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/transacted.properties new file mode 100644 index 0000000000..d93d19eea0 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/jmsexample/transacted/transacted.properties @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.transactedQueue = transactedQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.transactedTopic = transactedTopic \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java new file mode 100644 index 0000000000..1849f733e9 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.publisher; + +import java.io.File; + +import javax.jms.JMSException; + + +import org.apache.qpid.example.shared.FileUtils; +import org.apache.qpid.example.shared.Statics; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +/** + * Class that sends message files to the Publisher to distribute + * using files as input + * Must set properties for host in properties file or uses in vm broker + */ +public class FileMessageDispatcher +{ + + protected static final Logger _logger = LoggerFactory.getLogger(FileMessageDispatcher.class); + + protected static Publisher _publisher = null; + + /** + * To use this main method you need to specify a path or file to use for input + * This class then uses file contents from the dir/file specified to generate + * messages to publish + * Intended to be a very simple way to get going with publishing using the broker + * @param args - must specify one value, the path to file(s) for publisher + */ + public static void main(String[] args) + { + + // Check command line args ok - must provide a path or file for us to dispatch + if (args.length == 0) + { + System.out.println("Usage: FileMessageDispatcher " + ""); + } + else + { + try + { + // publish message(s) from file(s) to configured queue + publish(args[0]); + + // Move payload file(s) to archive location as no error + FileUtils.moveFileToNewDir(args[0], System.getProperties().getProperty(Statics.ARCHIVE_PATH)); + } + catch (Exception e) + { + // log error and exit + _logger.error("Error trying to dispatch message: " + e); + System.exit(1); + } + finally + { + // clean up before exiting + if (getPublisher() != null) + { + getPublisher().cleanup(); + } + } + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Finished dispatching message"); + } + + System.exit(0); + } + + /** + * Publish the content of a file or files from a directory as messages + * @param path - from main args + * @throws JMSException + * @throws MessageFactoryException - if cannot create message from file content + */ + public static void publish(String path) throws JMSException, MessageFactoryException + { + File tempFile = new File(path); + if (tempFile.isDirectory()) + { + // while more files in dir publish them + File[] files = tempFile.listFiles(); + + if ((files == null) || (files.length == 0)) + { + _logger.info("FileMessageDispatcher - No files to publish in input directory: " + tempFile); + } + else + { + for (File file : files) + { + // Create message factory passing in payload path + FileMessageFactory factory = new FileMessageFactory(getPublisher().getSession(), file.toString()); + + // Send the message generated from the payload using the _publisher + getPublisher().sendMessage(factory.createEventMessage()); + + } + } + } + else + { + // handle a single file + // Create message factory passing in payload path + FileMessageFactory factory = new FileMessageFactory(getPublisher().getSession(), tempFile.toString()); + + // Send the message generated from the payload using the _publisher + getPublisher().sendMessage(factory.createEventMessage()); + } + } + + /** + * Cleanup before exit + */ + public static void cleanup() + { + if (getPublisher() != null) + { + getPublisher().cleanup(); + } + } + + /** + * @return A Publisher instance + */ + private static Publisher getPublisher() + { + if (_publisher != null) + { + return _publisher; + } + + // Create a _publisher + _publisher = new Publisher(); + + return _publisher; + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java new file mode 100644 index 0000000000..1240284a56 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.qpid.example.publisher; + +import org.apache.qpid.example.shared.FileUtils; +import org.apache.qpid.example.shared.Statics; + +import java.io.*; +import javax.jms.*; + +public class FileMessageFactory +{ + protected final Session _session; + protected final String _payload; + protected final String _filename; + + /** + * Contructs and instance using a filename from which content will be used to create message + * @param session + * @param filename + * @throws MessageFactoryException + */ + public FileMessageFactory(Session session, String filename) throws MessageFactoryException + { + try + { + _filename = filename; + _payload = FileUtils.getFileContent(filename); + _session = session; + } + catch (IOException e) + { + MessageFactoryException mfe = new MessageFactoryException(e.toString(), e); + throw mfe; + } + } + + /** + * Creates a text message and sets filename property on it + * The filename property is purely intended to provide visibility + * of file content passing trhough the broker using example classes + * @return Message - a TextMessage with content from file + * @throws JMSException + */ + public Message createEventMessage() throws JMSException + { + TextMessage msg = _session.createTextMessage(); + msg.setText(_payload); + msg.setStringProperty(Statics.FILENAME_PROPERTY, new File(_filename).getName()); + + return msg; + } + + /** + * Creates message from a string for use by the monitor + * @param session + * @param textMsg - message content + * @return Message - TextMessage with content from String + * @throws JMSException + */ + public static Message createSimpleEventMessage(Session session, String textMsg) throws JMSException + { + TextMessage msg = session.createTextMessage(); + msg.setText(textMsg); + + return msg; + } + + public Message createShutdownMessage() throws JMSException + { + return _session.createTextMessage("SHUTDOWN"); + } + + public Message createReportRequestMessage() throws JMSException + { + return _session.createTextMessage("REPORT"); + } + + public Message createReportResponseMessage(String msg) throws JMSException + { + return _session.createTextMessage(msg); + } + + public boolean isShutdown(Message m) + { + return checkText(m, "SHUTDOWN"); + } + + public boolean isReport(Message m) + { + return checkText(m, "REPORT"); + } + + public Object getReport(Message m) + { + try + { + return ((TextMessage) m).getText(); + } + catch (JMSException e) + { + e.printStackTrace(System.out); + + return e.toString(); + } + } + + private static boolean checkText(Message m, String s) + { + try + { + return (m instanceof TextMessage) && ((TextMessage) m).getText().equals(s); + } + catch (JMSException e) + { + e.printStackTrace(System.out); + + return false; + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java new file mode 100644 index 0000000000..d709da6432 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.publisher; + +public class MessageFactoryException extends Exception +{ + public MessageFactoryException(String msg, Throwable t) + { + super(msg, t); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java new file mode 100644 index 0000000000..3d16e01af4 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.publisher; + + +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; + +/** + * Class that sends heartbeat messages to allow monitoring of message consumption Sends regular (currently 20 seconds + * apart) heartbeat message + */ +public class MonitorMessageDispatcher +{ + + private static final Logger _logger = LoggerFactory.getLogger(MonitorMessageDispatcher.class); + + protected static MonitorPublisher _monitorPublisher = null; + + protected static final String DEFAULT_MONITOR_PUB_NAME = "MonitorPublisher"; + + /** + * Easy entry point for running a message dispatcher for monitoring consumption + * Sends 1000 messages with no delay + * + * @param args + */ + public static void main(String[] args) + { + //Switch on logging appropriately for your app + try + { + int i =0; + while (i < 1000) + { + try + { + //endlessly publish messages to monitor queue + publish(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Dispatched monitor message"); + } + + //sleep for twenty seconds and then publish again - change if appropriate + //Thread.sleep(1000); + i++ ; + } + catch (UndeliveredMessageException a) + { + //trigger application specific failure handling here + _logger.error("Problem delivering monitor message"); + break; + } + } + } + catch (Exception e) + { + _logger.error("Error trying to dispatch AMS monitor message: " + e); + System.exit(1); + } + finally + { + if (getMonitorPublisher() != null) + { + getMonitorPublisher().cleanup(); + } + } + + System.exit(1); + } + + /** + * Publish heartbeat message + * + * @throws JMSException + * @throws UndeliveredMessageException + */ + public static void publish() throws JMSException, UndeliveredMessageException + { + //Send the message generated from the payload using the _publisher +// getMonitorPublisher().sendImmediateMessage +// (FileMessageFactory.createSimpleEventMessage(getMonitorPublisher().getSession(),"monitor:" +System.currentTimeMillis())); + + getMonitorPublisher().sendMessage + (getMonitorPublisher()._session, + FileMessageFactory.createSimpleEventMessage(getMonitorPublisher().getSession(), "monitor:" + System.currentTimeMillis()), + DeliveryMode.PERSISTENT, false, true); + + } + + /** Cleanup publishers */ + public static void cleanup() + { + if (getMonitorPublisher() != null) + { + getMonitorPublisher().cleanup(); + } + + if (getMonitorPublisher() != null) + { + getMonitorPublisher().cleanup(); + } + } + + //Returns a _publisher for the monitor queue + private static MonitorPublisher getMonitorPublisher() + { + if (_monitorPublisher != null) + { + return _monitorPublisher; + } + + //Create a _publisher using failover details and constant for monitor queue + _monitorPublisher = new MonitorPublisher(); + + _monitorPublisher.setName(MonitorMessageDispatcher.DEFAULT_MONITOR_PUB_NAME); + return _monitorPublisher; + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java new file mode 100644 index 0000000000..750f57d9dc --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.publisher; + +import org.apache.qpid.client.BasicMessageProducer; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; + +/** + * Subclass of Publisher which uses QPID functionality to send a heartbeat message Note immediate flag not available via + * JMS MessageProducer + */ +public class MonitorPublisher extends Publisher +{ + + private static final Logger _log = LoggerFactory.getLogger(Publisher.class); + + BasicMessageProducer _producer; + + public MonitorPublisher() + { + super(); + } + + /* + * Publishes a message using given details + */ + public boolean sendMessage(Session session, Message message, int deliveryMode, + boolean immediate, boolean commit) throws UndeliveredMessageException + { + try + { + _producer = (BasicMessageProducer) session.createProducer(_destination); + + _producer.send(message, deliveryMode, immediate); + + if (commit) + { + //commit the message send and close the transaction + _session.commit(); + } + + } + catch (JMSException e) + { + //Have to assume our commit failed but do not rollback here as channel closed + _log.error("JMSException", e); + e.printStackTrace(); + throw new UndeliveredMessageException("Cannot deliver immediate message", e); + } + + _log.info(_name + " finished sending message: " + message); + return true; + } + + /* + * Publishes a non-persistent message using transacted session + */ + public boolean sendImmediateMessage(Message message) throws UndeliveredMessageException + { + try + { + _producer = (BasicMessageProducer) _session.createProducer(_destination); + + //Send message via our producer which is not persistent and is immediate + //NB: not available via jms interface MessageProducer + _producer.send(message, DeliveryMode.NON_PERSISTENT, true); + + //commit the message send and close the transaction + _session.commit(); + + } + catch (JMSException e) + { + //Have to assume our commit failed but do not rollback here as channel closed + _log.error("JMSException", e); + e.printStackTrace(); + throw new UndeliveredMessageException("Cannot deliver immediate message", e); + } + + _log.info(_name + " finished sending message: " + message); + return true; + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java new file mode 100644 index 0000000000..87fc543dbe --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.publisher; + +import org.apache.qpid.client.AMQConnectionFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.DeliveryMode; +import javax.jms.Queue; +import javax.jms.MessageProducer; +import javax.jms.Connection; +import javax.jms.Session; + +import javax.naming.InitialContext; + +import org.apache.qpid.example.shared.InitialContextHelper; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +public class Publisher +{ + private static final Logger _log = LoggerFactory.getLogger(Publisher.class); + + protected InitialContextHelper _contextHelper; + + protected Connection _connection; + + protected Session _session; + + protected MessageProducer _producer; + + protected String _destinationDir; + + protected String _name = "Publisher"; + + protected Queue _destination; + + protected static final String _defaultDestinationDir = "/tmp"; + + /** + * Creates a Publisher instance using properties from example.properties + * See InitialContextHelper for details of how context etc created + */ + public Publisher() + { + try + { + //get an initial context from default properties + _contextHelper = new InitialContextHelper(null); + InitialContext ctx = _contextHelper.getInitialContext(); + + //then create a connection using the AMQConnectionFactory + AMQConnectionFactory cf = (AMQConnectionFactory) ctx.lookup("local"); + _connection = cf.createConnection(); + + //create a transactional session + _session = _connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + + //lookup the example queue and use it + //Queue is non-exclusive and not deleted when last consumer detaches + _destination = (Queue) ctx.lookup("MyQueue"); + + //create a message producer + _producer = _session.createProducer(_destination); + + //set destination dir for files that have been processed + _destinationDir = _defaultDestinationDir; + + _connection.start(); + } + catch (Exception e) + { + e.printStackTrace(); + _log.error("Exception", e); + } + } + + /** + * Publishes a non-persistent message using transacted session + * Note that persistent is the default mode for send - so need to specify for transient + */ + public boolean sendMessage(Message message) + { + try + { + //Send message via our producer which is not persistent + _producer.send(message, DeliveryMode.NON_PERSISTENT, _producer.getPriority(), _producer.getTimeToLive()); + + //commit the message send and close the transaction + _session.commit(); + + } + catch (JMSException e) + { + //Have to assume our commit failed and rollback here + try + { + _session.rollback(); + _log.error("JMSException", e); + e.printStackTrace(); + return false; + } + catch (JMSException j) + { + _log.error("Unable to rollback publish transaction ",e); + return false; + } + } + + _log.info(_name + " finished sending message: " + message); + return true; + } + + /** + * Cleanup resources before exit + */ + public void cleanup() + { + try + { + if (_connection != null) + { + _connection.stop(); + _connection.close(); + } + _connection = null; + _producer = null; + } + catch(Exception e) + { + _log.error("Error trying to cleanup publisher " + e); + System.exit(1); + } + } + + /** + * Exposes session + * @return Session + */ + public Session getSession() + { + return _session; + } + + public String getDestinationDir() + { + return _destinationDir; + } + + public void setDestinationDir(String destinationDir) + { + _destinationDir = destinationDir; + } + + public String getName() + { + return _name; + } + + public void setName(String _name) { + this._name = _name; + } +} + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java new file mode 100644 index 0000000000..245008b68a --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.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.example.publisher; + +/** + * Exception thrown by monitor when cannot send a message marked for immediate delivery + */ +public class UndeliveredMessageException extends Exception +{ + public UndeliveredMessageException(String msg, Throwable t) + { + super(msg, t); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java new file mode 100644 index 0000000000..e32ee0ba73 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.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.example.pubsub; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.naming.NamingException; + +/** + * An abstract base class that wraps up the creation of a JMS client utilising JNDI + */ +public abstract class Client +{ + protected ConnectionSetup _setup; + + protected Connection _connection; + protected Destination _destination; + protected Session _session; + + public Client(String destination) + { + if (destination == null) + { + destination = ConnectionSetup.TOPIC_JNDI_NAME; + } + + try + { + _setup = new ConnectionSetup(); + } + catch (NamingException e) + { + //ignore + } + + if (_setup != null) + { + try + { + _connection = _setup.getConnectionFactory().createConnection(); + _destination = _setup.getDestination(destination); + } + catch (JMSException e) + { + System.err.println(e.getMessage()); + } + } + } + + public abstract void start(); + +} \ No newline at end of file diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java new file mode 100644 index 0000000000..c4edd9034f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.example.pubsub; + +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Properties; + +/** + * This ConnectionSetup is a wrapper around JNDI it creates a number of entries. + * + * It is equivalent to a PropertyFile of value: + * + * connectionfactory.local=amqp://guest:guest@clientid/test?brokerlist='localhost' + * connectionfactory.vm=amqp://guest:guest@clientid/test?brokerlist='vm://:1' + * + * queue.queue=example.MyQueue + * topic.topic=example.hierarical.topic + * + */ +public class ConnectionSetup +{ + final static String INITIAL_CONTEXT_FACTORY = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; + + final static String CONNECTION_JNDI_NAME = "local"; + final static String CONNECTION_NAME = "amqp://guest:guest@clientid/test?brokerlist='localhost'"; + + public static final String QUEUE_JNDI_NAME = "queue"; + final static String QUEUE_NAME = "example.MyQueue"; + + public static final String TOPIC_JNDI_NAME = "topic"; + final static String TOPIC_NAME = "example.hierarical.topic"; + + private Context _ctx; + + public ConnectionSetup() throws NamingException + { + + // Set the properties ... + Properties properties = new Properties(); + properties.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY); + properties.put("connectionfactory." + CONNECTION_JNDI_NAME, CONNECTION_NAME); + properties.put("connectionfactory." + "vm", "amqp://guest:guest@clientid/test?brokerlist='vm://:1'"); + + properties.put("queue." + QUEUE_JNDI_NAME, QUEUE_NAME); + properties.put("topic." + TOPIC_JNDI_NAME, TOPIC_NAME); + // Create the initial context + _ctx = new InitialContext(properties); + + } + + public ConnectionSetup(Properties properties) throws NamingException + { + _ctx = new InitialContext(properties); + } + + public ConnectionFactory getConnectionFactory() + { + + // Perform the lookups + try + { + return (ConnectionFactory) _ctx.lookup(CONNECTION_JNDI_NAME); + } + catch (NamingException e) + { + //ignore + } + return null; + } + + public Destination getDestination(String jndiName) + { + // Perform the lookups + try + { + return (Destination) _ctx.lookup(jndiName); + } + catch (ClassCastException cce) + { + //ignore + } + catch (NamingException ne) + { + //ignore + } + return null; + } + + + public void close() + { + try + { + _ctx.close(); + } + catch (NamingException e) + { + //ignore + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java new file mode 100644 index 0000000000..dd936e429f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.example.pubsub; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * A simple Publisher example. + * + * The class can take two arguments. + * java Publisher + * Where: + * destination is either 'topic' or 'queue' (Default: topic) + * msgCount is the number of messages to send (Default : 100) + * + */ +public class Publisher extends Client +{ + int _msgCount; + + public Publisher(String destination, int msgCount) + { + super(destination); + _msgCount = msgCount; + } + + public void start() + { + try + { + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageProducer _producer = _session.createProducer(_destination); + + for (int msgCount = 0; msgCount < _msgCount; msgCount++) + { + _producer.send(_session.createTextMessage("msg:" + msgCount)); + System.out.println("Sent:" + msgCount); + } + + System.out.println("Done."); + _connection.close(); + } + catch (JMSException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + + public static void main(String[] args) + { + + String destination = args.length > 2 ? args[1] : null; + + int msgCount = args.length > 2 ? Integer.parseInt(args[2]) : 100; + + new Publisher(destination, msgCount).start(); + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java new file mode 100644 index 0000000000..f2d736701f --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.example.pubsub; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import java.util.concurrent.CountDownLatch; + + +/** + * Simple client that listens for the specified number of msgs on the given Destinaton + * + * The class can take two arguments. + * java Subscriber + * Where: + * destination is either 'topic' or 'queue' (Default: topic) + * msgCount is the number of messages to send (Default : 100) + */ +public class Subscriber extends Client implements MessageListener +{ + + CountDownLatch _count; + + public Subscriber(String destination, int msgCount) + { + super(destination); + _count = new CountDownLatch(msgCount); + } + + + public void start() + { + try + { + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _session.createDurableSubscriber((Topic) _setup.getDestination(ConnectionSetup.TOPIC_JNDI_NAME), + "exampleClient").setMessageListener(this); + _connection.start(); + _count.await(); + + System.out.println("Done"); + + _connection.close(); + } + catch (JMSException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + catch (InterruptedException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public static void main(String[] args) + { + String destination = args.length > 2 ? args[1] : null; + int msgCount = args.length > 2 ? Integer.parseInt(args[2]) : 100; + + new Subscriber(destination, msgCount).start(); + } + + public void onMessage(Message message) + { + try + { + _count.countDown(); + System.out.println("Received msg:" + ((TextMessage) message).getText()); + } + catch (JMSException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java new file mode 100644 index 0000000000..1a3d596a24 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.shared; + +public class ConnectionException extends Exception +{ + public ConnectionException(String msg, Throwable t) + { + super(msg, t); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java new file mode 100644 index 0000000000..2987a9559b --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.shared; + +public class ContextException extends Exception +{ + public ContextException(String msg, Throwable t) + { + super(msg, t); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java new file mode 100644 index 0000000000..54446cb6a7 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.shared; + +import java.io.*; + +/** + * Class that provides file related utility methods for utility use + */ +public class FileUtils { + + + //Reads file content into String + public static String getFileContent(String filePath) throws IOException + { + + BufferedReader reader = null; + String tempData = ""; + String eol = "\n\r"; + + try + { + String line; + reader = new BufferedReader(new FileReader(filePath)); + while ((line = reader.readLine()) != null) + { + if (!tempData.equals("")) + { + tempData = tempData + eol + line; + } + else + { + tempData = line; + } + } + } + finally + { + if (reader != null) + { + reader.close(); + } + } + return tempData; + } + + /* + * Reads xml from a file and returns it as an array of chars + */ + public static char[] getFileAsCharArray(String filePath) throws IOException + { + BufferedReader reader = null; + char[] tempChars = null; + String tempData = ""; + + try + { + String line; + reader = new BufferedReader(new FileReader(filePath)); + while ((line = reader.readLine()) != null) + { + tempData = tempData + line; + } + tempChars = tempData.toCharArray(); + } + finally + { + if (reader != null) + { + reader.close(); + } + } + return tempChars; + } + + /* + * Write String content to filename provided + */ + public static void writeStringToFile(String content, String path) throws IOException + { + + BufferedWriter writer = new BufferedWriter(new FileWriter(new File(path))); + writer.write(content); + writer.flush(); + writer.close(); + } + + /* + * Allows moving of files to a new dir and preserves the last bit of the name only + */ + public static void moveFileToNewDir(String path, String newDir) throws IOException + { + //get file name from current path + //while more files in dir publish them + File pathFile = new File(path); + if (pathFile.isDirectory()) + { + File[] files = pathFile.listFiles(); + for (File file : files) + { + moveFileToNewDir(file,newDir); + } + } + } + + /* + * Allows moving of a file to a new dir and preserves the last bit of the name only + */ + public static void moveFileToNewDir(File fileToMove, String newDir) throws IOException + { + moveFile(fileToMove,getArchiveFileName(fileToMove,newDir)); + } + + /* + * Moves file from a given path to a new path with String params + */ + public static void moveFile(String fromPath, String dest) throws IOException + { + moveFile(new File(fromPath),new File(dest)); + } + + /* + * Moves file from a given path to a new path with mixed params + */ + public static void moveFile(File fileToMove, String dest) throws IOException + { + moveFile(fileToMove,new File(dest)); + } + + /* + * Moves file from a given path to a new path with File params + */ + public static void moveFile(File fileToMove, File dest) throws IOException + { + fileToMove.renameTo(dest); + } + + /* + * Deletes a given file + */ + public static void deleteFile(String filePath) throws IOException + { + new File(filePath).delete(); + } + + private static String getArchiveFileName(File fileToMove, String archiveDir) + { + //get file name from current path + String fileName = fileToMove.getName(); + return archiveDir + File.separator + fileName; + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java new file mode 100644 index 0000000000..1328816602 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java @@ -0,0 +1,81 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.shared; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +/** + * Class that provides helper methods for JNDI + */ +public class InitialContextHelper +{ + + public static final String _defaultPropertiesName = "example.properties"; + protected static Properties _fileProperties; + protected static InitialContext _initialContext; + protected static final Logger _log = LoggerFactory.getLogger(InitialContextHelper.class); + + public InitialContextHelper(String propertiesName) throws ContextException + { + try + { + if ((propertiesName == null) || (propertiesName.length() == 0)) + { + propertiesName = _defaultPropertiesName; + } + + _fileProperties = new Properties(); + ClassLoader cl = this.getClass().getClassLoader(); + + // NB: Need to change path to reflect package if moving classes around ! + InputStream is = cl.getResourceAsStream("org/apache/qpid/example/shared/" + propertiesName); + _fileProperties.load(is); + _initialContext = new InitialContext(_fileProperties); + } + catch (IOException e) + { + throw new ContextException(e.toString(), e); + } + catch (NamingException n) + { + throw new ContextException(n.toString(), n); + } + } + + public Properties getFileProperties() + { + return _fileProperties; + } + + public InitialContext getInitialContext() + { + return _initialContext; + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java new file mode 100644 index 0000000000..c056f8a7da --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.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.example.shared; + +/** + * Constants used by AMS Publisher/Subscriber classes + */ +public class Statics { + + public static final String TOPIC_NAME = "EXAMPLE_TOPIC"; + + public static final String QUEUE_NAME = "EXAMPLE_QUEUE"; + + public static final String MONITOR_QUEUE_SUFFIX = "_MONITOR"; + + public static final String HOST_PROPERTY = "host"; + + public static final String PORT_PROPERTY = "port"; + + public static final String USER_PROPERTY = "user"; + + public static final String PWD_PROPERTY = "pwd"; + + public static final String TOPIC_PROPERTY = "topic"; + + public static final String QUEUE_PROPERTY = "queue"; + + public static final String VIRTUAL_PATH_PROPERTY = "virtualpath"; + + public static final String ARCHIVE_PATH = "archivepath"; + + public static final String CLIENT_PROPERTY = "client"; + + public static final String FILENAME_PROPERTY = "filename"; + + public static final String DEFAULT_USER = "guest"; + + public static final String DEFAULT_PWD = "guest"; + + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties new file mode 100644 index 0000000000..a60e3964ad --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties @@ -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. + + +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# use the following property to configure the default connector +#java.naming.provider.url - ignored. + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.local = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.ibmStocks = stocks.nyse.ibm + +# Register an AMQP destination in JNDI +# NOTE: Qpid currently only supports direct,topics and headers +# destination.[jniName] = [BindingURL] +destination.direct = direct://amq.direct//directQueue diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Client.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Client.java new file mode 100644 index 0000000000..8a0ff88448 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Client.java @@ -0,0 +1,263 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.example.simple.reqresp; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +public class Client implements MessageListener +{ + final String BROKER = "localhost"; + + final String INITIAL_CONTEXT_FACTORY = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; + + final String CONNECTION_JNDI_NAME = "local"; + final String CONNECTION_NAME = "amqp://guest:guest@clientid/test?brokerlist='" + BROKER + "'"; + + final String QUEUE_JNDI_NAME = "queue"; + final String QUEUE_NAME = "example.RequestQueue"; + + + private InitialContext _ctx; + + private CountDownLatch _shutdownHook = new CountDownLatch(1); + + public Client() + { + setupJNDI(); + + Connection connection; + Session session; + Destination responseQueue; + + //Setup the connection. Create producer to sent message and consumer to receive the repsonse. + MessageProducer _producer; + try + { + connection = ((ConnectionFactory) lookupJNDI(CONNECTION_JNDI_NAME)).createConnection(); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Destination requestQueue = (Queue) lookupJNDI(QUEUE_JNDI_NAME); + + closeJNDI(); + + //Setup a message _producer to send message to the queue the server is consuming from + _producer = session.createProducer(requestQueue); + + //Create a temporary queue that this client will listen for responses on then create a consumer + //that consumes message from this temporary queue. + responseQueue = session.createTemporaryQueue(); + + MessageConsumer responseConsumer = session.createConsumer(responseQueue); + + //Set a listener to asynchronously deal with responses. + responseConsumer.setMessageListener(this); + + // Now the connection is setup up start it. + connection.start(); + } + catch (JMSException e) + { + System.err.println("Unable to setup connection, client and producer on broker"); + return; + } + + // Setup the message to send + TextMessage txtMessage; + try + { + //Now create the actual message you want to send + txtMessage = session.createTextMessage("Request Process"); + + //Set the reply to field to the temp queue you created above, this is the queue the server will respond to + txtMessage.setJMSReplyTo(responseQueue); + + //Set a correlation ID so when you get a response you know which sent message the response is for + //If there is never more than one outstanding message to the server then the + //same correlation ID can be used for all the messages...if there is more than one outstanding + //message to the server you would presumably want to associate the correlation ID with this message + + txtMessage.setJMSCorrelationID(txtMessage.getJMSMessageID()); + } + catch (JMSException e) + { + System.err.println("Unable to create message"); + return; + + } + + try + { + _producer.send(txtMessage); + } + catch (JMSException e) + { + //Handle the exception appropriately + } + + try + { + System.out.println("Sent Request Message ID :" + txtMessage.getJMSMessageID()); + } + catch (JMSException e) + { + //Handle exception more appropriately. + } + + //Wait for the return message to arrive + try + { + _shutdownHook.await(); + } + catch (InterruptedException e) + { + // Ignore this as we are quitting anyway. + } + + //Close the connection + try + { + connection.close(); + } + catch (JMSException e) + { + System.err.println("A problem occured while shutting down the connection : " + e); + } + } + + + /** + * Implementation of the Message Listener interface. + * This is where message will be asynchronously delivered. + * + * @param message + */ + public void onMessage(Message message) + { + String messageText; + try + { + if (message instanceof TextMessage) + { + TextMessage textMessage = (TextMessage) message; + messageText = textMessage.getText(); + System.out.println("messageText = " + messageText); + System.out.println("Correlation ID " + message.getJMSCorrelationID()); + + _shutdownHook.countDown(); + } + else + { + System.err.println("Unexpected message delivered"); + } + } + catch (JMSException e) + { + //Handle the exception appropriately + } + } + + /** + * Lookup the specified name in the JNDI Context. + * + * @param name The string name of the object to lookup + * + * @return The object or null if nothing exists for specified name + */ + private Object lookupJNDI(String name) + { + try + { + return _ctx.lookup(name); + } + catch (NamingException e) + { + System.err.println("Error looking up '" + name + "' in JNDI Context:" + e); + } + + return null; + } + + /** + * Setup the JNDI context. + * + * In this case we are simply using a Properties object to store the pairing information. + * + * Further details can be found on the wiki site here: + * + * @see : http://cwiki.apache.org/qpid/how-to-use-jndi.html + */ + private void setupJNDI() + { + // Set the properties ... + Properties properties = new Properties(); + properties.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY); + properties.put("connectionfactory." + CONNECTION_JNDI_NAME, CONNECTION_NAME); + properties.put("queue." + QUEUE_JNDI_NAME, QUEUE_NAME); + + // Create the initial context + Context ctx = null; + try + { + _ctx = new InitialContext(properties); + } + catch (NamingException e) + { + System.err.println("Error Setting up JNDI Context:" + e); + } + } + + /** Close the JNDI Context to keep everything happy. */ + private void closeJNDI() + { + try + { + _ctx.close(); + } + catch (NamingException e) + { + System.err.println("Unable to close JNDI Context : " + e); + } + } + + + public static void main(String[] args) + { + new Client(); + } +} + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Server.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Server.java new file mode 100644 index 0000000000..9c284eee97 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/simple/reqresp/Server.java @@ -0,0 +1,236 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.example.simple.reqresp; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.io.BufferedReader; +import java.io.BufferedInputStream; +import java.io.Reader; +import java.io.InputStreamReader; +import java.io.IOException; + +public class Server implements MessageListener +{ + final String BROKER = "localhost"; + + final String INITIAL_CONTEXT_FACTORY = "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"; + + final String CONNECTION_JNDI_NAME = "local"; + final String CONNECTION_NAME = "amqp://guest:guest@clientid/test?brokerlist='" + BROKER + "'"; + + final String QUEUE_JNDI_NAME = "queue"; + final String QUEUE_NAME = "example.RequestQueue"; + + + private InitialContext _ctx; + private Session _session; + private MessageProducer _replyProducer; + private CountDownLatch _shutdownHook = new CountDownLatch(1); + + public Server() + { + setupJNDI(); + + Connection connection; + try + { + connection = ((ConnectionFactory) lookupJNDI(CONNECTION_JNDI_NAME)).createConnection(); + + _session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + Destination requestQueue = (Queue) lookupJNDI(QUEUE_JNDI_NAME); + + closeJNDI(); + + //Setup a message producer to respond to messages from clients, we will get the destination + //to send to from the JMSReplyTo header field from a Message so we MUST set the destination here to null. + this._replyProducer = _session.createProducer(null); + + //Set up a consumer to consume messages off of the request queue + MessageConsumer consumer = _session.createConsumer(requestQueue); + consumer.setMessageListener(this); + + //Now start the connection + connection.start(); + } + catch (JMSException e) + { + //Handle the exception appropriately + System.err.println("JMSException occured setting up server :" + e); + return; + } + + System.out.println("Server process started and waiting for messages."); + + //Wait to process an single message then quit. + while (_shutdownHook.getCount() != 0) + { + try + { + _shutdownHook.await(); + } + catch (InterruptedException e) + { + // Ignore this as we are quitting anyway. + } + } + + //Close the connection + try + { + connection.close(); + } + catch (JMSException e) + { + System.err.println("A problem occured while shutting down the connection : " + e); + } + } + + public void onMessage(Message message) + { + try + { + TextMessage response = this._session.createTextMessage(); + + //Check we have the right message type. + if (message instanceof TextMessage) + { + TextMessage txtMsg = (TextMessage) message; + String messageText = txtMsg.getText(); + + //Perform the request + System.out.println("Received request:" + messageText + " for message :" + message.getJMSMessageID()); + + //Set the response back to the client + response.setText("Response to Request:" + messageText); + } + + //Set the correlation ID from the received message to be the correlation id of the response message + //this lets the client identify which message this is a response to if it has more than + //one outstanding message to the server + response.setJMSCorrelationID(message.getJMSMessageID()); + + try + { + System.out.println("Received message press enter to send response...."); + new BufferedReader(new InputStreamReader(System.in)).readLine(); + } + catch (IOException e) + { + //Error attemptying to pause + } + + //Send the response to the Destination specified by the JMSReplyTo field of the received message. + _replyProducer.send(message.getJMSReplyTo(), response); + } + catch (JMSException e) + { + //Handle the exception appropriately + } + + _shutdownHook.countDown(); + } + + /** + * Lookup the specified name in the JNDI Context. + * + * @param name The string name of the object to lookup + * + * @return The object or null if nothing exists for specified name + */ + private Object lookupJNDI(String name) + { + try + { + return _ctx.lookup(name); + } + catch (NamingException e) + { + System.err.println("Error looking up '" + name + "' in JNDI Context:" + e); + } + + return null; + } + + /** + * Setup the JNDI context. + * + * In this case we are simply using a Properties object to store the pairing information. + * + * Further details can be found on the wiki site here: + * + * @see : http://cwiki.apache.org/qpid/how-to-use-jndi.html + */ + private void setupJNDI() + { + // Set the properties ... + Properties properties = new Properties(); + properties.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY); + properties.put("connectionfactory." + CONNECTION_JNDI_NAME, CONNECTION_NAME); + properties.put("queue." + QUEUE_JNDI_NAME, QUEUE_NAME); + + // Create the initial context + Context ctx = null; + try + { + _ctx = new InitialContext(properties); + } + catch (NamingException e) + { + System.err.println("Error Setting up JNDI Context:" + e); + } + } + + /** Close the JNDI Context to keep everything happy. */ + private void closeJNDI() + { + try + { + _ctx.close(); + } + catch (NamingException e) + { + System.err.println("Unable to close JNDI Context : " + e); + } + } + + + public static void main(String[] args) + { + new Server(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java new file mode 100644 index 0000000000..d43b823a13 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.subscriber; + +import org.apache.qpid.example.shared.Statics; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jms.*; +/** + * Subclass of Subscriber which consumes a heartbeat message + */ + +public class MonitoredSubscriber extends Subscriber +{ + protected String _monitorDestinationName; + + private static final Logger _logger = LoggerFactory.getLogger(MonitoredSubscriber.class); + + private static MessageConsumer _monitorConsumer; + + public MonitoredSubscriber() + { + super(); + //lookup queue name and append suffix + _monitorDestinationName = _destination.toString() + Statics.MONITOR_QUEUE_SUFFIX; + } + + /** + * MessageListener implementation for this subscriber + */ + public static class MonitorMessageListener implements MessageListener + { + private String _name; + + public MonitorMessageListener(String name) + { + _name = name; + + } + + /** + * Listens for heartbeat messages and acknowledges them + * @param message + */ + public void onMessage(javax.jms.Message message) + { + _logger.info(_name + " monitor got message '" + message + "'"); + + try + { + _logger.debug("Monitor acknowledging recieved message"); + + //Now acknowledge the message to clear it from our queue + message.acknowledge(); + } + catch(JMSException j) + { + _logger.error("Monitor caught JMSException trying to acknowledge message receipt"); + j.printStackTrace(); + } + catch(Exception e) + { + _logger.error("Monitor caught unexpected exception trying to handle message"); + e.printStackTrace(); + } + } + } + + /** + * Subscribes to Queue and attaches additional monitor listener + */ + public void subscribeAndMonitor() + { + try + { + _connection = _connectionFactory.createConnection(); + + //create a transactional session + Session session = _connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + + //Queue is non-exclusive and not deleted when last consumer detaches + Destination destination = session.createQueue(_monitorDestinationName); + + //Create a consumer with a destination of our queue which will use defaults for prefetch etc + _monitorConsumer = session.createConsumer(destination); + + //give the monitor message listener a name of it's own + _monitorConsumer.setMessageListener(new MonitoredSubscriber.MonitorMessageListener + ("MonitorListener " + System.currentTimeMillis())); + + MonitoredSubscriber._logger.info("Starting monitored subscription ..."); + + MonitoredSubscriber._connection.start(); + + //and now start ordinary consumption too + subscribe(); + } + catch (Throwable t) + { + _logger.error("Fatal error: " + t); + t.printStackTrace(); + } + } + + /** + * Stop consuming + */ + public void stopMonitor() + { + try + { + _monitorConsumer.close(); + _monitorConsumer = null; + stop(); + } + catch(JMSException j) + { + _logger.error("JMSException trying to Subscriber.stop: " + j.getStackTrace()); + } + } + +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java new file mode 100644 index 0000000000..5e78107182 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.subscriber; + + +/** + * Allows you to simply start a monitored subscriber + */ +public class MonitoredSubscriptionWrapper { + + private static MonitoredSubscriber _subscriber; + + /** + * Create a monitored subscriber and start it + * @param args - no params required + */ + public static void main(String args[]) + { + _subscriber = new MonitoredSubscriber(); + + _subscriber.subscribe(); + } + + /** + * Stop subscribing now ... + */ + public static void stop() + { + _subscriber.stop(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java new file mode 100644 index 0000000000..f75558299c --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.example.subscriber; + +import org.apache.qpid.client.AMQConnectionFactory; + +import javax.jms.*; +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.naming.InitialContext; + +import org.apache.qpid.example.shared.InitialContextHelper; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +/** + * Subscriber which consumes messages from a queue + */ + +public class Subscriber +{ + private static final Logger _log = LoggerFactory.getLogger(Subscriber.class); + + protected static Connection _connection; + + protected static MessageConsumer _consumer; + + protected static InitialContextHelper _contextHelper; + + protected static AMQConnectionFactory _connectionFactory; + + protected Destination _destination; + + public Subscriber() + { + try + { + //get an initial context from default properties + _contextHelper = new InitialContextHelper(null); + InitialContext ctx = _contextHelper.getInitialContext(); + + //then create a connection using the AMQConnectionFactory + _connectionFactory = (AMQConnectionFactory) ctx.lookup("local"); + + //lookup queue from context + _destination = (Destination) ctx.lookup("MyQueue"); + + } + catch (Exception e) + { + e.printStackTrace(); + _log.error("Exception", e); + } + } + + /** + * Listener class that handles messages + */ + public static class ExampleMessageListener implements MessageListener + { + private String _name; + + public ExampleMessageListener(String name) + { + _name = name; + } + + /** + * Listens for message callbacks, handles and then acknowledges them + * @param message - the message received + */ + public void onMessage(javax.jms.Message message) + { + _log.info(_name + " got message '" + message + "'"); + + try + { + //NB: Handle your message appropriately for your application here + //do some stuff + + _log.debug("Acknowledging recieved message"); + + //Now acknowledge the message to clear it from our queue + message.acknowledge(); + } + catch(JMSException j) + { + _log.error("JMSException trying to acknowledge message receipt"); + j.printStackTrace(); + } + catch(Exception e) + { + _log.error("Unexpected exception trying to handle message"); + e.printStackTrace(); + } + } + } + + /** + * Subscribes to example Queue and attaches listener + */ + public void subscribe() + { + _log.info("Starting subscription ..."); + + try + { + _connection = _connectionFactory.createConnection(); + + //Non transactional session using client acknowledgement + Session session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + //Create a consumer with a destination of our queue which will use defaults for prefetch etc + _consumer = session.createConsumer(_destination); + + //give the message listener a name of it's own + _consumer.setMessageListener(new ExampleMessageListener("MessageListener " + System.currentTimeMillis())); + + _connection.start(); + } + catch (Throwable t) + { + _log.error("Fatal error: " + t); + t.printStackTrace(); + } + + _log.info("Waiting for messages ..."); + + //wait for messages and sleep to survive failover + try + { + while(true) + { + Thread.sleep(Long.MAX_VALUE); + } + } + catch (Exception e) + { + _log.warn("Exception while Subscriber sleeping",e); + } + } + + /** + * Stop consuming and close connection + */ + public void stop() + { + try + { + _consumer.close(); + _consumer = null; + _connection.stop(); + _connection.close(); + } + catch(JMSException j) + { + _log.error("JMSException trying to Subscriber.stop: " + j.getStackTrace()); + } + } + +} + + + + diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java new file mode 100644 index 0000000000..f8fbf63037 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.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.example.subscriber; + +/** + * Allows you to simply start a subscriber + */ +public class SubscriptionWrapper { + + private static Subscriber _subscriber; + + /** + * Create a subscriber and start it + * @param args + */ + public static void main(String args[]) + { + _subscriber = new Subscriber(); + + _subscriber.subscribe(); + } + + /** + * Stop subscribing now ... + */ + public static void stop() + { + _subscriber.stop(); + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java new file mode 100644 index 0000000000..d7eb138523 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/org/apache/qpid/example/transport/ExistingSocketConnectorDemo.java @@ -0,0 +1,171 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.example.transport; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.SocketChannel; +import java.util.UUID; + +/** + * This is a simple application that demonstrates how you can use the Qpid AMQP interfaces to use existing sockets as + * the transport for the Client API. + * + * The Demo here runs twice: + * 1. Just to show a simple publish and receive. + * 2. To demonstrate how to use existing sockets and utilise the underlying client failover mechnaism. + */ +public class ExistingSocketConnectorDemo implements ConnectionListener +{ + private static boolean DEMO_FAILOVER = false; + + public static void main(String[] args) throws IOException, URLSyntaxException, AMQException, JMSException + { + System.out.println("Testing socket connection to localhost:5672."); + + new ExistingSocketConnectorDemo(); + + System.out.println("Testing socket connection failover between localhost:5672 and localhost:5673."); + + DEMO_FAILOVER = true; + + new ExistingSocketConnectorDemo(); + } + + Connection _connection; + MessageProducer _producer; + Session _session; + + String Socket1_ID = UUID.randomUUID().toString(); + String Socket2_ID = UUID.randomUUID().toString(); + + + + /** Here we can see the broker we are connecting to is set to be 'socket:///' signifying we will provide the socket. */ + public final String CONNECTION = "amqp://guest:guest@id/test?brokerlist='socket://" + Socket1_ID + ";socket://" + Socket2_ID + "'"; + + + public ExistingSocketConnectorDemo() throws IOException, URLSyntaxException, AMQException, JMSException + { + + Socket socket = SocketChannel.open().socket(); + socket.connect(new InetSocketAddress("localhost", 5672)); + + TransportConnection.registerOpenSocket(Socket1_ID, socket); + + + _connection = new AMQConnection(CONNECTION); + + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageConsumer consumer = _session.createConsumer(_session.createQueue("Queue")); + + _producer = _session.createProducer(_session.createQueue("Queue")); + + _connection.start(); + + if (!DEMO_FAILOVER) + { + _producer.send(_session.createTextMessage("Simple Test")); + } + else + { + // Using the Qpid interfaces we can set a listener that allows us to demonstrate failover + ((AMQConnection) _connection).setConnectionListener(this); + + System.out.println("Testing failover: Please ensure second broker running on localhost:5673 and shutdown broker on 5672."); + } + + //We do a blocking receive here so that we can demonstrate failover. + Message message = consumer.receive(); + + System.out.println("Recevied :" + message); + + _connection.close(); + } + + // ConnectionListener Interface + + public void bytesSent(long count) + { + //not used in this example + } + public void bytesReceived(long count) + { + //not used in this example + } + + public boolean preFailover(boolean redirect) + { + /** + * This method is called before the underlying client library starts to reconnect. This gives us the opportunity + * to set a new socket for the failover to occur on. + */ + try + { + Socket socket = SocketChannel.open().socket(); + + socket.connect(new InetSocketAddress("localhost", 5673)); + + // This is the new method to pass in an open socket for the connection to use. + TransportConnection.registerOpenSocket(Socket2_ID, socket); + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean preResubscribe() + { + //not used in this example - but must return true to allow the resubscription of existing clients. + return true; + } + + public void failoverComplete() + { + // Now that failover has completed we can send a message that the receiving thread will pick up + try + { + _producer.send(_session.createTextMessage("Simple Failover Test")); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } +} diff --git a/RC6/qpid/java/client/example/src/main/java/runSample.sh b/RC6/qpid/java/client/example/src/main/java/runSample.sh new file mode 100755 index 0000000000..e330fb0c36 --- /dev/null +++ b/RC6/qpid/java/client/example/src/main/java/runSample.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# Work out the CLASSPATH divider +UNAME=`uname -s` +case $UNAME in + CYGWIN*) + DIVIDER=";" + ;; + *) + DIVIDER=":" +;; +esac + +if test "'x$QPID_HOME'" != "'x'" +then + QPID_HOME=$QPID_HOME +else + QPID_HOME="/usr/share/java/" +fi +echo "Using QPID_HOME: $QPID_HOME" + +if test "'x$QPID_SAMPLE'" != "'x'" +then + QPID_SAMPLE=$QPID_SAMPLE +else + QPID_SAMPLE="/usr/share/doc/rhm-0.2" +fi +echo "Using QPID_SAMPLE: $QPID_SAMPLE" + + +# set the CLASSPATH +CLASSPATH=`find "$QPID_HOME" -name '*.jar' | tr '\n' "$DIVIDER"` + + +# compile the samples +javac -cp "$CLASSPATH" -sourcepath "$QPID_SAMPLE" -d . `find $QPID_SAMPLE -name '*.java'` + +# Add output classes to CLASSPATH +CLASSPATH="$CLASSPATH$DIVIDER$." + +# Set VM parameters +QPID_PARAM="$QPID_PARAM -Dlog4j.configuration=file://$PWD/log4j.xml" + + +# Check if the user supplied a sample classname +if test "'x$1'" = "'x'" +then + echo "No sample classname specified" + exit; +else + java -cp $CLASSPATH $QPID_PARAM $* +fi diff --git a/RC6/qpid/java/client/src/main/grammar/SelectorParser.jj b/RC6/qpid/java/client/src/main/grammar/SelectorParser.jj new file mode 100644 index 0000000000..b45cf1a487 --- /dev/null +++ b/RC6/qpid/java/client/src/main/grammar/SelectorParser.jj @@ -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. + * + */ + + // + // Original File from r450141 of the Apache ActiveMQ project + // + +// ---------------------------------------------------------------------------- +// OPTIONS +// ---------------------------------------------------------------------------- +options { + STATIC = false; + UNICODE_INPUT = true; + + // some performance optimizations + OPTIMIZE_TOKEN_MANAGER = true; + ERROR_REPORTING = false; +} + +// ---------------------------------------------------------------------------- +// PARSER +// ---------------------------------------------------------------------------- + +PARSER_BEGIN(SelectorParser) +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.filter.selector; + +import java.io.StringReader; +import java.util.ArrayList; + +import org.apache.qpid.QpidException; +import org.apache.qpid.filter.ArithmeticExpression; +import org.apache.qpid.filter.BooleanExpression; +import org.apache.qpid.filter.ComparisonExpression; +import org.apache.qpid.filter.ConstantExpression; +import org.apache.qpid.filter.Expression; +import org.apache.qpid.filter.LogicExpression; +import org.apache.qpid.filter.PropertyExpression; +import org.apache.qpid.filter.UnaryExpression; + +/** + * JMS Selector Parser generated by JavaCC + * + * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj + */ +public class SelectorParser { + + public SelectorParser() { + this(new StringReader("")); + } + + public BooleanExpression parse(String sql) throws QpidException { + this.ReInit(new StringReader(sql)); + + try { + return this.JmsSelector(); + } + catch (Throwable e) { + throw new QpidException(sql,null,e); + } + + } + + private BooleanExpression asBooleanExpression(Expression value) throws ParseException { + if (value instanceof BooleanExpression) { + return (BooleanExpression) value; + } + if (value instanceof PropertyExpression) { + return UnaryExpression.createBooleanCast( value ); + } + throw new ParseException("Expression will not result in a boolean value: " + value); + } + + +} + +PARSER_END(SelectorParser) + +// ---------------------------------------------------------------------------- +// Tokens +// ---------------------------------------------------------------------------- + +/* White Space */ +SPECIAL_TOKEN : +{ + " " | "\t" | "\n" | "\r" | "\f" +} + +/* Comments */ +SKIP: +{ + +} + +SKIP: +{ + +} + +/* Reserved Words */ +TOKEN [IGNORE_CASE] : +{ + < NOT : "NOT"> + | < AND : "AND"> + | < OR : "OR"> + | < BETWEEN : "BETWEEN"> + | < LIKE : "LIKE"> + | < ESCAPE : "ESCAPE"> + | < IN : "IN"> + | < IS : "IS"> + | < TRUE : "TRUE" > + | < FALSE : "FALSE" > + | < NULL : "NULL" > +} + +/* Literals */ +TOKEN [IGNORE_CASE] : +{ + + < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? > + | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > + | < OCTAL_LITERAL: "0" (["0"-"7"])* > + | < FLOATING_POINT_LITERAL: + (["0"-"9"])+ "." (["0"-"9"])* ()? // matches: 5.5 or 5. or 5.5E10 or 5.E10 + | "." (["0"-"9"])+ ()? // matches: .5 or .5E10 + | (["0"-"9"])+ // matches: 5E10 + > + | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ > + | < STRING_LITERAL: "'" ( ("''") | ~["'"] )* "'" > +} + +TOKEN [IGNORE_CASE] : +{ + < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* > + | < QUOTED_ID : "\"" ( ("\"\"") | ~["\""] )* "\"" > + +} + +// ---------------------------------------------------------------------------- +// Grammer +// ---------------------------------------------------------------------------- +BooleanExpression JmsSelector() : +{ + Expression left=null; +} +{ + ( + left = orExpression() + ) + { + return asBooleanExpression(left); + } + +} + +Expression orExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = andExpression() + ( + right = andExpression() + { + left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } + +} + + +Expression andExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = equalityExpression() + ( + right = equalityExpression() + { + left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } +} + +Expression equalityExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = comparisonExpression() + ( + + "=" right = comparisonExpression() + { + left = ComparisonExpression.createEqual(left, right); + } + | + "<>" right = comparisonExpression() + { + left = ComparisonExpression.createNotEqual(left, right); + } + | + LOOKAHEAD(2) + + { + left = ComparisonExpression.createIsNull(left); + } + | + + { + left = ComparisonExpression.createIsNotNull(left); + } + )* + ) + { + return left; + } +} + +Expression comparisonExpression() : +{ + Expression left; + Expression right; + Expression low; + Expression high; + String t, u; + boolean not; + ArrayList list; +} +{ + ( + left = addExpression() + ( + + ">" right = addExpression() + { + left = ComparisonExpression.createGreaterThan(left, right); + } + | + ">=" right = addExpression() + { + left = ComparisonExpression.createGreaterThanEqual(left, right); + } + | + "<" right = addExpression() + { + left = ComparisonExpression.createLessThan(left, right); + } + | + "<=" right = addExpression() + { + left = ComparisonExpression.createLessThanEqual(left, right); + } + | + { + u=null; + } + t = stringLitteral() + [ u = stringLitteral() ] + { + left = ComparisonExpression.createLike(left, t, u); + } + | + LOOKAHEAD(2) + { + u=null; + } + t = stringLitteral() [ u = stringLitteral() ] + { + left = ComparisonExpression.createNotLike(left, t, u); + } + | + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createBetween(left, low, high); + } + | + LOOKAHEAD(2) + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createNotBetween(left, low, high); + } + | + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createInFilter(left, list); + } + | + LOOKAHEAD(2) + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createNotInFilter(left, list); + } + + )* + ) + { + return left; + } +} + +Expression addExpression() : +{ + Expression left; + Expression right; +} +{ + left = multExpr() + ( + LOOKAHEAD( ("+"|"-") multExpr()) + ( + "+" right = multExpr() + { + left = ArithmeticExpression.createPlus(left, right); + } + | + "-" right = multExpr() + { + left = ArithmeticExpression.createMinus(left, right); + } + ) + + )* + { + return left; + } +} + +Expression multExpr() : +{ + Expression left; + Expression right; +} +{ + left = unaryExpr() + ( + "*" right = unaryExpr() + { + left = ArithmeticExpression.createMultiply(left, right); + } + | + "/" right = unaryExpr() + { + left = ArithmeticExpression.createDivide(left, right); + } + | + "%" right = unaryExpr() + { + left = ArithmeticExpression.createMod(left, right); + } + + )* + { + return left; + } +} + + +Expression unaryExpr() : +{ + String s=null; + Expression left=null; +} +{ + ( + LOOKAHEAD( "+" unaryExpr() ) + "+" left=unaryExpr() + | + "-" left=unaryExpr() + { + left = UnaryExpression.createNegate(left); + } + | + left=unaryExpr() + { + left = UnaryExpression.createNOT( asBooleanExpression(left) ); + } + | + left = primaryExpr() + ) + { + return left; + } + +} + +Expression primaryExpr() : +{ + Expression left=null; +} +{ + ( + left = literal() + | + left = variable() + | + "(" left = orExpression() ")" + ) + { + return left; + } +} + + + +ConstantExpression literal() : +{ + Token t; + String s; + ConstantExpression left=null; +} +{ + ( + ( + s = stringLitteral() + { + left = new ConstantExpression(s); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromDecimal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromHex(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromOctal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFloat(t.image); + } + ) + | + ( + + { + left = ConstantExpression.TRUE; + } + ) + | + ( + + { + left = ConstantExpression.FALSE; + } + ) + | + ( + + { + left = ConstantExpression.NULL; + } + ) + ) + { + return left; + } +} + +String stringLitteral() : +{ + Token t; + StringBuffer rc = new StringBuffer(); + boolean first=true; +} +{ + t = + { + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '\'' ) + i++; + rc.append(c); + } + return rc.toString(); + } +} + +PropertyExpression variable() : +{ + Token t; + StringBuffer rc = new StringBuffer(); + PropertyExpression left=null; +} +{ + ( + t = + { + left = new PropertyExpression(t.image); + } + | + t = + { + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '"' ) + i++; + rc.append(c); + } + return new PropertyExpression(rc.toString()); + } + + ) + { + return left; + } +} diff --git a/RC6/qpid/java/client/src/main/java/client.bnd b/RC6/qpid/java/client/src/main/java/client.bnd new file mode 100755 index 0000000000..dbda0aad3b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/client.bnd @@ -0,0 +1,6 @@ +ver: M4 + +Bundle-SymbolicName: qpid_client +Bundle-Version: ${ver} +Export-Package: *;version=${ver} +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/RC6/qpid/java/client/src/main/java/client.log4j b/RC6/qpid/java/client/src/main/java/client.log4j new file mode 100644 index 0000000000..19cc946118 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/client.log4j @@ -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. +# +log4j.rootLogger=${root.logging.level} + + +#log4j.logger.org.apache.qpid=${amqj.logging.level}, console +#log4j.additivity.org.apache.qpid=false + +log4j.logger.org.apache.qpid=ERROR, console +log4j.additivity.org.apache.qpid=false + +#log4j.logger.org.apache.qpid.client.message.AbstractBytesTypedMessage=DEBUG, console + +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.Threshold=all +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n diff --git a/RC6/qpid/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java b/RC6/qpid/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java new file mode 100644 index 0000000000..98716c0c3c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/mina/transport/socket/nio/ExistingSocketConnector.java @@ -0,0 +1,478 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoConnectorConfig; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.support.BaseIoConnector; +import org.apache.mina.common.support.DefaultConnectFuture; +import org.apache.mina.util.NamePreservingRunnable; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.Queue; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Set; + +/** + * {@link IoConnector} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 627427 $, $Date: 2008-02-13 14:39:10 +0000 (Wed, 13 Feb 2008) $ + */ +public class ExistingSocketConnector extends BaseIoConnector +{ + /** @noinspection StaticNonFinalField */ + private static volatile int nextId = 0; + + private final Object lock = new Object(); + private final int id = nextId++; + private final String threadName = "SocketConnector-" + id; + private SocketConnectorConfig defaultConfig = new SocketConnectorConfig(); + private final Queue connectQueue = new Queue(); + private final SocketIoProcessor[] ioProcessors; + private final int processorCount; + private final Executor executor; + + /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */ + private Selector selector; + private Worker worker; + private int processorDistributor = 0; + private int workerTimeout = 60; // 1 min. + private Socket _openSocket = null; + + /** Create a connector with a single processing thread using a NewThreadExecutor */ + public ExistingSocketConnector() + { + this(1, new NewThreadExecutor()); + } + + /** + * Create a connector with the desired number of processing threads + * + * @param processorCount Number of processing threads + * @param executor Executor to use for launching threads + */ + public ExistingSocketConnector(int processorCount, Executor executor) + { + if (processorCount < 1) + { + throw new IllegalArgumentException("Must have at least one processor"); + } + + this.executor = executor; + this.processorCount = processorCount; + ioProcessors = new SocketIoProcessor[processorCount]; + + for (int i = 0; i < processorCount; i++) + { + ioProcessors[i] = new SocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor); + } + } + + /** + * How many seconds to keep the connection thread alive between connection requests + * + * @return Number of seconds to keep connection thread alive + */ + public int getWorkerTimeout() + { + return workerTimeout; + } + + /** + * Set how many seconds the connection worker thread should remain alive once idle before terminating itself. + * + * @param workerTimeout Number of seconds to keep thread alive. Must be >=0 + */ + public void setWorkerTimeout(int workerTimeout) + { + if (workerTimeout < 0) + { + throw new IllegalArgumentException("Must be >= 0"); + } + this.workerTimeout = workerTimeout; + } + + public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config) + { + return connect(address, null, handler, config); + } + + public ConnectFuture connect(SocketAddress address, SocketAddress localAddress, + IoHandler handler, IoServiceConfig config) + { + /** Changes here from the Mina OpenSocketConnector. + * Ignoreing all address as they are not needed */ + + if (handler == null) + { + throw new NullPointerException("handler"); + } + + + if (config == null) + { + config = getDefaultConfig(); + } + + if (_openSocket == null) + { + throw new IllegalArgumentException("Specifed Socket not active"); + } + + boolean success = false; + + try + { + DefaultConnectFuture future = new DefaultConnectFuture(); + newSession(_openSocket, handler, config, future); + success = true; + return future; + } + catch (IOException e) + { + return DefaultConnectFuture.newFailedFuture(e); + } + finally + { + if (!success && _openSocket != null) + { + try + { + _openSocket.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + public IoServiceConfig getDefaultConfig() + { + return defaultConfig; + } + + /** + * Sets the config this connector will use by default. + * + * @param defaultConfig the default config. + * + * @throws NullPointerException if the specified value is null. + */ + public void setDefaultConfig(SocketConnectorConfig defaultConfig) + { + if (defaultConfig == null) + { + throw new NullPointerException("defaultConfig"); + } + this.defaultConfig = defaultConfig; + } + + private synchronized void startupWorker() throws IOException + { + if (worker == null) + { + selector = Selector.open(); + worker = new Worker(); + executor.execute(new NamePreservingRunnable(worker)); + } + } + + private void registerNew() + { + if (connectQueue.isEmpty()) + { + return; + } + + for (; ;) + { + ConnectionRequest req; + synchronized (connectQueue) + { + req = (ConnectionRequest) connectQueue.pop(); + } + + if (req == null) + { + break; + } + + SocketChannel ch = req.channel; + try + { + ch.register(selector, SelectionKey.OP_CONNECT, req); + } + catch (IOException e) + { + req.setException(e); + } + } + } + + private void processSessions(Set keys) + { + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isConnectable()) + { + continue; + } + + SocketChannel ch = (SocketChannel) key.channel(); + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + boolean success = false; + try + { + ch.finishConnect(); + newSession(ch, entry.handler, entry.config, entry); + success = true; + } + catch (Throwable e) + { + entry.setException(e); + } + finally + { + key.cancel(); + if (!success) + { + try + { + ch.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + keys.clear(); + } + + private void processTimedOutSessions(Set keys) + { + long currentTime = System.currentTimeMillis(); + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isValid()) + { + continue; + } + + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + if (currentTime >= entry.deadline) + { + entry.setException(new ConnectException()); + try + { + key.channel().close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + key.cancel(); + } + } + } + } + + private void newSession(Socket socket, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + { + SocketSessionImpl session = new SocketSessionImpl(this, + nextProcessor(), + getListeners(), + config, + socket.getChannel(), + handler, + socket.getRemoteSocketAddress()); + + newSession(session, config, connectFuture); + } + + private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + + { + SocketSessionImpl session = new SocketSessionImpl(this, + nextProcessor(), + getListeners(), + config, + ch, + handler, + ch.socket().getRemoteSocketAddress()); + + newSession(session, config, connectFuture); + } + + private void newSession(SocketSessionImpl session, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + { + try + { + getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getThreadModel().buildFilterChain(session.getFilterChain()); + } + catch (Throwable e) + { + throw (IOException) new IOException("Failed to create a session.").initCause(e); + } + session.getIoProcessor().addNew(session); + connectFuture.setSession(session); + } + + private SocketIoProcessor nextProcessor() + { + return ioProcessors[processorDistributor++ % processorCount]; + } + + public void setOpenSocket(Socket openSocket) + { + _openSocket = openSocket; + } + + private class Worker implements Runnable + { + private long lastActive = System.currentTimeMillis(); + + public void run() + { + Thread.currentThread().setName(ExistingSocketConnector.this.threadName); + + for (; ;) + { + try + { + int nKeys = selector.select(1000); + + registerNew(); + + if (nKeys > 0) + { + processSessions(selector.selectedKeys()); + } + + processTimedOutSessions(selector.keys()); + + if (selector.keys().isEmpty()) + { + if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L) + { + synchronized (lock) + { + if (selector.keys().isEmpty() && + connectQueue.isEmpty()) + { + worker = null; + try + { + selector.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + selector = null; + } + break; + } + } + } + } + else + { + lastActive = System.currentTimeMillis(); + } + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + ExceptionMonitor.getInstance().exceptionCaught(e1); + } + } + } + } + } + + private class ConnectionRequest extends DefaultConnectFuture + { + private final SocketChannel channel; + private final long deadline; + private final IoHandler handler; + private final IoServiceConfig config; + + private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config) + { + this.channel = channel; + long timeout; + if (config instanceof IoConnectorConfig) + { + timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis(); + } + else + { + timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis(); + } + this.deadline = System.currentTimeMillis() + timeout; + this.handler = handler; + this.config = config; + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java new file mode 100644 index 0000000000..05ac3dca9e --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQAuthenticationException represents all failures to authenticate access to a broker. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to authenticate the client. + *
+ * + * @todo Will this alwyas have the same status code, NOT_ALLOWED 530? Might set this up to always use that code. + */ +public class AMQAuthenticationException extends AMQException +{ + public AMQAuthenticationException(AMQConstant error, String msg, Throwable cause) + { + super(error, msg, cause); + } + public boolean isHardError() + { + return true; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java new file mode 100644 index 0000000000..414638dea4 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java @@ -0,0 +1,390 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.URLHelper; +import org.apache.qpid.url.URLSyntaxException; + +public class AMQBrokerDetails implements BrokerDetails +{ + private String _host; + private int _port; + private String _transport; + + private Map _options = new HashMap(); + + private SSLConfiguration _sslConfiguration; + + public AMQBrokerDetails(){} + + public AMQBrokerDetails(String url) throws URLSyntaxException + { + + // URL should be of format tcp://host:port?option='value',option='value' + try + { + URI connection = new URI(url); + + String transport = connection.getScheme(); + + // Handles some defaults to minimise changes to existing broker URLS e.g. localhost + if (transport != null) + { + //todo this list of valid transports should be enumerated somewhere + if ((!(transport.equalsIgnoreCase(BrokerDetails.VM) || + transport.equalsIgnoreCase(BrokerDetails.TCP) || + transport.equalsIgnoreCase(BrokerDetails.SOCKET)))) + { + if (transport.equalsIgnoreCase("localhost")) + { + connection = new URI(DEFAULT_TRANSPORT + "://" + url); + transport = connection.getScheme(); + } + else + { + if (url.charAt(transport.length()) == ':' && url.charAt(transport.length() + 1) != '/') + { + //Then most likely we have a host:port value + connection = new URI(DEFAULT_TRANSPORT + "://" + url); + transport = connection.getScheme(); + } + else + { + throw URLHelper.parseError(0, transport.length(), "Unknown transport", url); + } + } + } + else if (url.indexOf("//") == -1) + { + throw new URLSyntaxException(url, "Missing '//' after the transport In broker URL",transport.length()+1,1); + } + } + else + { + //Default the transport + connection = new URI(DEFAULT_TRANSPORT + "://" + url); + transport = connection.getScheme(); + } + + if (transport == null) + { + throw URLHelper.parseError(-1, "Unknown transport:'" + transport + "'" + + " In broker URL:'" + url + "' Format: " + URL_FORMAT_EXAMPLE, ""); + } + + setTransport(transport); + + String host = connection.getHost(); + + // Fix for Java 1.5 + if (host == null) + { + host = ""; + } + + setHost(host); + + int port = connection.getPort(); + + if (port == -1) + { + // Fix for when there is port data but it is not automatically parseable by getPort(). + String auth = connection.getAuthority(); + + if (auth != null && auth.contains(":")) + { + int start = auth.indexOf(":") + 1; + int end = start; + boolean looking = true; + boolean found = false; + // Throw an URL exception if the port number is not specified + if (start == auth.length()) + { + throw URLHelper.parseError(connection.toString().indexOf(auth) + end - 1, + connection.toString().indexOf(auth) + end, "Port number must be specified", + connection.toString()); + } + //Walk the authority looking for a port value. + while (looking) + { + try + { + end++; + Integer.parseInt(auth.substring(start, end)); + + if (end >= auth.length()) + { + looking = false; + found = true; + } + } + catch (NumberFormatException nfe) + { + looking = false; + } + + } + if (found) + { + setPort(Integer.parseInt(auth.substring(start, end))); + } + else + { + throw URLHelper.parseError(connection.toString().indexOf(connection.getAuthority()) + end - 1, + "Illegal character in port number", connection.toString()); + } + + } + else + { + setPort(DEFAULT_PORT); + } + } + else + { + if (!_transport.equalsIgnoreCase(SOCKET)) + { + setPort(port); + } + } + + String queryString = connection.getQuery(); + + URLHelper.parseOptions(_options, queryString); + + //Fragment is #string (not used) + } + catch (URISyntaxException uris) + { + if (uris instanceof URLSyntaxException) + { + throw(URLSyntaxException) uris; + } + + throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput()); + } + } + + public AMQBrokerDetails(String host, int port, SSLConfiguration sslConfiguration) + { + _host = host; + _port = port; + _sslConfiguration = sslConfiguration; + } + + public String getHost() + { + return _host; + } + + public void setHost(String _host) + { + this._host = _host; + } + + public int getPort() + { + return _port; + } + + public void setPort(int _port) + { + this._port = _port; + } + + public String getTransport() + { + return _transport; + } + + public void setTransport(String _transport) + { + this._transport = _transport; + } + + + public String getProperty(String key) + { + return _options.get(key); + } + + public void setProperty(String key, String value) + { + _options.put(key, value); + } + + public long getTimeout() + { + if (_options.containsKey(OPTIONS_CONNECT_TIMEOUT)) + { + try + { + return Long.parseLong(_options.get(OPTIONS_CONNECT_TIMEOUT)); + } + catch (NumberFormatException nfe) + { + //Do nothing as we will use the default below. + } + } + + return BrokerDetails.DEFAULT_CONNECT_TIMEOUT; + } + + public boolean useSSL() + { + if (_options.containsKey(ConnectionURL.OPTIONS_SSL)) + { + return Boolean.parseBoolean(_options.get(ConnectionURL.OPTIONS_SSL)); + } + + return false; + } + + public void setTimeout(long timeout) + { + setProperty(OPTIONS_CONNECT_TIMEOUT, Long.toString(timeout)); + } + + public SSLConfiguration getSSLConfiguration() + { + return _sslConfiguration; + } + + public void setSSLConfiguration(SSLConfiguration sslConfig) + { + _sslConfiguration = sslConfig; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + + sb.append(_transport); + sb.append("://"); + + if (!(_transport.equalsIgnoreCase(VM))) + { + sb.append(_host); + } + + if (!(_transport.equalsIgnoreCase(SOCKET))) + { + sb.append(':'); + sb.append(_port); + } + + sb.append(printOptionsURL()); + + return sb.toString(); + } + + public boolean equals(Object o) + { + if (!(o instanceof BrokerDetails)) + { + return false; + } + + BrokerDetails bd = (BrokerDetails) o; + + return _host.equalsIgnoreCase(bd.getHost()) && + (_port == bd.getPort()) && + _transport.equalsIgnoreCase(bd.getTransport()) && + compareSSLConfigurations(bd.getSSLConfiguration()); + //todo do we need to compare all the options as well? + } + + private String printOptionsURL() + { + StringBuffer optionsURL = new StringBuffer(); + + optionsURL.append('?'); + + if (!(_options.isEmpty())) + { + + for (String key : _options.keySet()) + { + optionsURL.append(key); + + optionsURL.append("='"); + + optionsURL.append(_options.get(key)); + + optionsURL.append("'"); + + optionsURL.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + } + + //removeKey the extra DEFAULT_OPTION_SEPERATOR or the '?' if there are no options + optionsURL.deleteCharAt(optionsURL.length() - 1); + + return optionsURL.toString(); + } + + // Do we need to do a more in-depth comparison? + private boolean compareSSLConfigurations(SSLConfiguration other) + { + boolean retval = false; + if (_sslConfiguration == null && + other == null) + { + retval = true; + } + else if (_sslConfiguration != null && + other != null) + { + retval = true; + } + + return retval; + } + + public static String checkTransport(String broker) + { + if ((!broker.contains("://"))) + { + return "tcp://" + broker; + } + else + { + return broker; + } + } + + public Map getProperties() + { + return _options; + } + + public void setProperties(Map props) + { + _options = props; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java new file mode 100644 index 0000000000..4e8fdc2370 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -0,0 +1,1454 @@ +/* +* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQConnectionFailureException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQProtocolException; +import org.apache.qpid.AMQUnresolvedAddressException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.configuration.ClientProperties; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.*; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.Connection; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.FailoverPolicy; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.ConnectException; +import java.net.UnknownHostException; +import java.nio.channels.UnresolvedAddressException; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable +{ + public static final class ChannelToSessionMap + { + private final AMQSession[] _fastAccessSessions = new AMQSession[16]; + private final LinkedHashMap _slowAccessSessions = new LinkedHashMap(); + private int _size = 0; + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + + public AMQSession get(int channelId) + { + if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + return _fastAccessSessions[channelId]; + } + else + { + return _slowAccessSessions.get(channelId); + } + } + + public AMQSession put(int channelId, AMQSession session) + { + AMQSession oldVal; + if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + oldVal = _fastAccessSessions[channelId]; + _fastAccessSessions[channelId] = session; + } + else + { + oldVal = _slowAccessSessions.put(channelId, session); + } + if ((oldVal != null) && (session == null)) + { + _size--; + } + else if ((oldVal == null) && (session != null)) + { + _size++; + } + + return session; + + } + + public AMQSession remove(int channelId) + { + AMQSession session; + if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + session = _fastAccessSessions[channelId]; + _fastAccessSessions[channelId] = null; + } + else + { + session = _slowAccessSessions.remove(channelId); + } + + if (session != null) + { + _size--; + } + return session; + + } + + public Collection values() + { + ArrayList values = new ArrayList(size()); + + for (int i = 0; i < 16; i++) + { + if (_fastAccessSessions[i] != null) + { + values.add(_fastAccessSessions[i]); + } + } + values.addAll(_slowAccessSessions.values()); + + return values; + } + + public int size() + { + return _size; + } + + public void clear() + { + _size = 0; + _slowAccessSessions.clear(); + for (int i = 0; i < 16; i++) + { + _fastAccessSessions[i] = null; + } + } + } + + private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class); + + protected AtomicInteger _idFactory = new AtomicInteger(0); + + /** + * This is the "root" mutex that must be held when doing anything that could be impacted by failover. This must be + * held by any child objects of this connection such as the session, producers and consumers. + */ + private final Object _failoverMutex = new Object(); + + private final Object _sessionCreationLock = new Object(); + + /** + * A channel is roughly analogous to a session. The server can negotiate the maximum number of channels per session + * and we must prevent the client from opening too many. Zero means unlimited. + */ + protected long _maximumChannelCount; + + /** The maximum size of frame supported by the server */ + private long _maximumFrameSize; + + /** + * The protocol handler dispatches protocol events for this connection. For example, when the connection is dropped + * the handler deals with this. It also deals with the initial dispatch of any protocol frames to their appropriate + * handler. + */ + protected AMQProtocolHandler _protocolHandler; + + /** Maps from session id (Integer) to AMQSession instance */ + private final ChannelToSessionMap _sessions = new ChannelToSessionMap(); + + private String _clientName; + + /** The user name to use for authentication */ + private String _username; + + /** The password to use for authentication */ + private String _password; + + /** The virtual path to connect to on the AMQ server */ + private String _virtualHost; + + protected ExceptionListener _exceptionListener; + + private ConnectionListener _connectionListener; + + private ConnectionURL _connectionURL; + + /** + * Whether this connection is started, i.e. whether messages are flowing to consumers. It has no meaning for message + * publication. + */ + protected volatile boolean _started; + + /** Policy dictating how to failover */ + protected FailoverPolicy _failoverPolicy; + + /* + * _Connected should be refactored with a suitable wait object. + */ + protected boolean _connected; + + /* + * The connection meta data + */ + private QpidConnectionMetaData _connectionMetaData; + + /** Configuration info for SSL */ + private SSLConfiguration _sslConfiguration; + + private AMQShortString _defaultTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; + private AMQShortString _defaultQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; + private AMQShortString _temporaryTopicExchangeName = ExchangeDefaults.TOPIC_EXCHANGE_NAME; + private AMQShortString _temporaryQueueExchangeName = ExchangeDefaults.DIRECT_EXCHANGE_NAME; + + /** Thread Pool for executing connection level processes. Such as returning bounced messages. */ + private final ExecutorService _taskPool = Executors.newCachedThreadPool(); + private static final long DEFAULT_TIMEOUT = 1000 * 30; + private ProtocolVersion _protocolVersion = ProtocolVersion.v0_9; // FIXME TGM, shouldn't need this + + protected AMQConnectionDelegate _delegate; + + // this connection maximum number of prefetched messages + protected int _maxPrefetch; + + //Indicates whether persistent messages are synchronized + private boolean _syncPersistence; + + /** + * @param broker brokerdetails + * @param username username + * @param password password + * @param clientName clientid + * @param virtualHost virtualhost + * + * @throws AMQException + * @throws URLSyntaxException + */ + public AMQConnection(String broker, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + this(new AMQConnectionURL( + ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='" + + AMQBrokerDetails.checkTransport(broker) + "'"), null); + } + + /** + * @param broker brokerdetails + * @param username username + * @param password password + * @param clientName clientid + * @param virtualHost virtualhost + * + * @throws AMQException + * @throws URLSyntaxException + */ + public AMQConnection(String broker, String username, String password, String clientName, String virtualHost, + SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + { + this(new AMQConnectionURL( + ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + + ((clientName == null) ? "" : clientName) + "/" + virtualHost + "?brokerlist='" + + AMQBrokerDetails.checkTransport(broker) + "'"), sslConfig); + } + + public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + this(host, port, false, username, password, clientName, virtualHost, null); + } + + public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost, + SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + { + this(host, port, false, username, password, clientName, virtualHost, sslConfig); + } + + public AMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName, + String virtualHost, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + { + this(new AMQConnectionURL( + useSSL + ? (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port + + "'" + "," + ConnectionURL.OPTIONS_SSL + "='true'") + : (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port + + "'" + "," + ConnectionURL.OPTIONS_SSL + "='false'")), sslConfig); + } + + public AMQConnection(String connection) throws AMQException, URLSyntaxException + { + this(new AMQConnectionURL(connection), null); + } + + public AMQConnection(String connection, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + { + this(new AMQConnectionURL(connection), sslConfig); + } + + /** + * @todo Some horrible stuff going on here with setting exceptions to be non-null to detect if an exception + * was thrown during the connection! Intention not clear. Use a flag anyway, not exceptions... Will fix soon. + */ + public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException + { + // set this connection maxPrefetch + if (connectionURL.getOption(ConnectionURL.AMQ_MAXPREFETCH) != null) + { + _maxPrefetch = Integer.parseInt(connectionURL.getOption(ConnectionURL.AMQ_MAXPREFETCH)); + } + else + { + // use the defaul value set for all connections + _maxPrefetch = Integer.parseInt(System.getProperties().getProperty(ClientProperties.MAX_PREFETCH_PROP_NAME, + ClientProperties.MAX_PREFETCH_DEFAULT)); + } + + if (connectionURL.getOption(ConnectionURL.AMQ_SYNC_PERSISTENCE) != null) + { + _syncPersistence = Boolean.parseBoolean(connectionURL.getOption(ConnectionURL.AMQ_SYNC_PERSISTENCE)); + } + else + { + // use the defaul value set for all connections + _syncPersistence = Boolean.getBoolean(ClientProperties.SYNC_PERSISTENT_PROP_NAME); + } + + _failoverPolicy = new FailoverPolicy(connectionURL); + BrokerDetails brokerDetails = _failoverPolicy.getNextBrokerDetails(); + if (brokerDetails.getTransport().equals(BrokerDetails.VM)) + { + _delegate = new AMQConnectionDelegate_8_0(this); + } + else + { + _delegate = new AMQConnectionDelegate_0_10(this); + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Connection:" + connectionURL); + } + + _sslConfiguration = sslConfig; + if (connectionURL == null) + { + throw new IllegalArgumentException("Connection must be specified"); + } + + _connectionURL = connectionURL; + + _clientName = connectionURL.getClientName(); + _username = connectionURL.getUsername(); + _password = connectionURL.getPassword(); + + setVirtualHost(connectionURL.getVirtualHost()); + + if (connectionURL.getDefaultQueueExchangeName() != null) + { + _defaultQueueExchangeName = connectionURL.getDefaultQueueExchangeName(); + } + + if (connectionURL.getDefaultTopicExchangeName() != null) + { + _defaultTopicExchangeName = connectionURL.getDefaultTopicExchangeName(); + } + + if (connectionURL.getTemporaryQueueExchangeName() != null) + { + _temporaryQueueExchangeName = connectionURL.getTemporaryQueueExchangeName(); + } + + if (connectionURL.getTemporaryTopicExchangeName() != null) + { + _temporaryTopicExchangeName = connectionURL.getTemporaryTopicExchangeName(); + } + + _protocolHandler = new AMQProtocolHandler(this); + + // We are not currently connected + _connected = false; + + boolean retryAllowed = true; + Exception connectionException = null; + while (!_connected && retryAllowed) + { + ProtocolVersion pe = null; + try + { + pe = makeBrokerConnection(brokerDetails); + } + catch (Exception e) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Unable to connect to broker at " + + _failoverPolicy.getCurrentBrokerDetails(), + e); + } + connectionException = e; + } + + if (pe != null) + { + // reset the delegate to the version returned by the + // broker + initDelegate(pe); + } + else if (!_connected) + { + retryAllowed = _failoverPolicy.failoverAllowed(); + brokerDetails = _failoverPolicy.getNextBrokerDetails(); + } + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Are we connected:" + _connected); + } + + if (!_connected) + { + String message = null; + + if (connectionException != null) + { + if (connectionException.getCause() != null) + { + message = connectionException.getCause().getMessage(); + connectionException.getCause().printStackTrace(); + } + else + { + message = connectionException.getMessage(); + } + } + + if ((message == null) || message.equals("")) + { + if (message == null) + { + message = "Unable to Connect"; + } + else // can only be "" if getMessage() returned it therfore lastException != null + { + message = "Unable to Connect:" + connectionException.getClass(); + } + } + + for (Throwable th = connectionException; th != null; th = th.getCause()) + { + if (th instanceof UnresolvedAddressException || + th instanceof UnknownHostException) + { + throw new AMQUnresolvedAddressException + (message, + _failoverPolicy.getCurrentBrokerDetails().toString(), + connectionException); + } + } + + throw new AMQConnectionFailureException(message, connectionException); + } + + _connectionMetaData = new QpidConnectionMetaData(this); + } + + protected boolean checkException(Throwable thrown) + { + Throwable cause = thrown.getCause(); + + if (cause == null) + { + cause = thrown; + } + + return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException)); + } + + private void initDelegate(ProtocolVersion pe) throws AMQProtocolException + { + try + { + Class c = Class.forName(String.format + ("org.apache.qpid.client.AMQConnectionDelegate_%s_%s", + pe.getMajorVersion(), pe.getMinorVersion())); + Class partypes[] = new Class[1]; + partypes[0] = AMQConnection.class; + _delegate = (AMQConnectionDelegate) c.getConstructor(partypes).newInstance(this); + } + catch (ClassNotFoundException e) + { + throw new AMQProtocolException + (AMQConstant.UNSUPPORTED_CLIENT_PROTOCOL_ERROR, + String.format("Protocol: %s.%s is rquired by the broker but is not " + + "currently supported by this client library implementation", + pe.getMajorVersion(), pe.getMinorVersion()), + e); + } + catch (NoSuchMethodException e) + { + throw new RuntimeException("unable to locate constructor for delegate", e); + } + catch (InstantiationException e) + { + throw new RuntimeException("error instantiating delegate", e); + } + catch (IllegalAccessException e) + { + throw new RuntimeException("error accessing delegate", e); + } + catch (InvocationTargetException e) + { + throw new RuntimeException("error invoking delegate", e); + } + } + + protected AMQConnection(String username, String password, String clientName, String virtualHost) + { + _clientName = clientName; + _username = username; + _password = password; + setVirtualHost(virtualHost); + } + + private void setVirtualHost(String virtualHost) + { + if (virtualHost != null && virtualHost.startsWith("/")) + { + virtualHost = virtualHost.substring(1); + } + + _virtualHost = virtualHost; + } + + public boolean attemptReconnection(String host, int port) + { + BrokerDetails bd = new AMQBrokerDetails(host, port, _sslConfiguration); + + _failoverPolicy.setBroker(bd); + + try + { + makeBrokerConnection(bd); + + return true; + } + catch (Exception e) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Unable to connect to broker at " + bd); + } + + attemptReconnection(); + } + + return false; + } + + public boolean attemptReconnection() + { + while (_failoverPolicy.failoverAllowed()) + { + try + { + makeBrokerConnection(_failoverPolicy.getNextBrokerDetails()); + + return true; + } + catch (Exception e) + { + if (!(e instanceof AMQException)) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(), e); + } + } + else + { + if (_logger.isInfoEnabled()) + { + _logger.info(e.getMessage() + ":Unable to connect to broker at " + + _failoverPolicy.getCurrentBrokerDetails()); + } + } + } + } + + // connection unsuccessful + return false; + } + + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + { + return _delegate.makeBrokerConnection(brokerDetail); + } + + public T executeRetrySupport(FailoverProtectedOperation operation) throws E + { + return _delegate.executeRetrySupport(operation); + } + + /** + * Get the details of the currently active broker + * + * @return null if no broker is active (i.e. no successful connection has been made, or the BrokerDetail instance + * otherwise + */ + public BrokerDetails getActiveBrokerDetails() + { + return _failoverPolicy.getCurrentBrokerDetails(); + } + + public boolean failoverAllowed() + { + if (!_connected) + { + return false; + } + else + { + return _failoverPolicy.failoverAllowed(); + } + } + + public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode) throws JMSException + { + return createSession(transacted, acknowledgeMode, _maxPrefetch); + } + + public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch) + throws JMSException + { + return createSession(transacted, acknowledgeMode, prefetch, prefetch); + } + + public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, + final int prefetchHigh, final int prefetchLow) throws JMSException + { + synchronized (_sessionCreationLock) + { + checkNotClosed(); + return _delegate.createSession(transacted, acknowledgeMode, prefetchHigh, prefetchLow); + } + } + + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + + ChannelOpenBody channelOpenBody = getProtocolHandler().getMethodRegistry().createChannelOpenBody(null); + + // TODO: Be aware of possible changes to parameter order as versions change. + + _protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class); + + BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(0, prefetchHigh, false); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(basicQosBody.generateFrame(channelId), BasicQosOkBody.class); + + if (transacted) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Issuing TxSelect for " + channelId); + } + + TxSelectBody body = getProtocolHandler().getMethodRegistry().createTxSelectBody(); + + // TODO: Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class); + } + } + + private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + try + { + createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); + } + catch (AMQException e) + { + deregisterSession(channelId); + throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e); + } + } + + public void setFailoverPolicy(FailoverPolicy policy) + { + _failoverPolicy = policy; + } + + public FailoverPolicy getFailoverPolicy() + { + return _failoverPolicy; + } + + /** + * Returns an AMQQueueSessionAdaptor which wraps an AMQSession and throws IllegalStateExceptions where specified in + * the JMS spec + * + * @param transacted + * @param acknowledgeMode + * + * @return QueueSession + * + * @throws JMSException + */ + public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException + { + return new AMQQueueSessionAdaptor(createSession(transacted, acknowledgeMode)); + } + + /** + * Returns an AMQTopicSessionAdapter which wraps an AMQSession and throws IllegalStateExceptions where specified in + * the JMS spec + * + * @param transacted + * @param acknowledgeMode + * + * @return TopicSession + * + * @throws JMSException + */ + public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException + { + return new AMQTopicSessionAdaptor(createSession(transacted, acknowledgeMode)); + } + + public boolean channelLimitReached() + { + return (_maximumChannelCount != 0) && (_sessions.size() == _maximumChannelCount); + } + + public String getClientID() throws JMSException + { + checkNotClosed(); + + return _clientName; + } + + public void setClientID(String clientID) throws JMSException + { + checkNotClosed(); + // in AMQP it is not possible to change the client ID. If one is not specified + // upon connection construction, an id is generated automatically. Therefore + // we can always throw an exception. + if (!Boolean.getBoolean(ClientProperties.IGNORE_SET_CLIENTID_PROP_NAME)) + { + throw new IllegalStateException("Client name cannot be changed after being set"); + } + else + { + _logger.info("Operation setClientID is ignored using ID: " + getClientID()); + } + } + + public ConnectionMetaData getMetaData() throws JMSException + { + checkNotClosed(); + + return _connectionMetaData; + + } + + public ExceptionListener getExceptionListener() throws JMSException + { + checkNotClosed(); + + return _exceptionListener; + } + + public void setExceptionListener(ExceptionListener listener) throws JMSException + { + checkNotClosed(); + _exceptionListener = listener; + } + + /** + * Start the connection, i.e. start flowing messages. Note that this method must be called only from a single thread + * and is not thread safe (which is legal according to the JMS specification). + * + * @throws JMSException + */ + public void start() throws JMSException + { + checkNotClosed(); + if (!_started) + { + _started = true; + final Iterator it = _sessions.values().iterator(); + while (it.hasNext()) + { + final AMQSession s = (AMQSession) (it.next()); + try + { + s.start(); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + } + + } + } + + public void stop() throws JMSException + { + checkNotClosed(); + if (_started) + { + for (Iterator i = _sessions.values().iterator(); i.hasNext();) + { + try + { + ((AMQSession) i.next()).stop(); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + } + + _started = false; + } + } + + public void close() throws JMSException + { + close(DEFAULT_TIMEOUT); + } + + public void close(long timeout) throws JMSException + { + close(new ArrayList(_sessions.values()), timeout); + } + + public void close(List sessions, long timeout) throws JMSException + { + if (!_closed.getAndSet(true)) + { + doClose(sessions, timeout); + } + } + + private void doClose(List sessions, long timeout) throws JMSException + { + synchronized (_sessionCreationLock) + { + if (!sessions.isEmpty()) + { + AMQSession session = sessions.remove(0); + synchronized (session.getMessageDeliveryLock()) + { + doClose(sessions, timeout); + } + } + else + { + synchronized (getFailoverMutex()) + { + try + { + long startCloseTime = System.currentTimeMillis(); + + closeAllSessions(null, timeout, startCloseTime); + + //This MUST occur after we have successfully closed all Channels/Sessions + _taskPool.shutdown(); + + if (!_taskPool.isTerminated()) + { + try + { + // adjust timeout + long taskPoolTimeout = adjustTimeout(timeout, startCloseTime); + + _taskPool.awaitTermination(taskPoolTimeout, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + _logger.info("Interrupted while shutting down connection thread pool."); + } + } + + // adjust timeout + timeout = adjustTimeout(timeout, startCloseTime); + _delegate.closeConnection(timeout); + + //If the taskpool hasn't shutdown by now then give it shutdownNow. + // This will interupt any running tasks. + if (!_taskPool.isTerminated()) + { + List tasks = _taskPool.shutdownNow(); + for (Runnable r : tasks) + { + _logger.warn("Connection close forced taskpool to prevent execution:" + r); + } + } + } + catch (AMQException e) + { + _logger.error("error:", e); + JMSException jmse = new JMSException("Error closing connection: " + e); + jmse.setLinkedException(e); + throw jmse; + } + } + } + } + } + + private long adjustTimeout(long timeout, long startTime) + { + long now = System.currentTimeMillis(); + timeout -= now - startTime; + if (timeout < 0) + { + timeout = 0; + } + + return timeout; + } + + /** + * Marks all sessions and their children as closed without sending any protocol messages. Useful when you need to + * mark objects "visible" in userland as closed after failover or other significant event that impacts the + * connection.

The caller must hold the failover mutex before calling this method. + */ + private void markAllSessionsClosed() + { + final LinkedList sessionCopy = new LinkedList(_sessions.values()); + final Iterator it = sessionCopy.iterator(); + while (it.hasNext()) + { + final AMQSession session = (AMQSession) it.next(); + + session.markClosed(); + } + + _sessions.clear(); + } + + /** + * Close all the sessions, either due to normal connection closure or due to an error occurring. + * + * @param cause if not null, the error that is causing this shutdown

The caller must hold the failover mutex + * before calling this method. + */ + private void closeAllSessions(Throwable cause, long timeout, long starttime) throws JMSException + { + final LinkedList sessionCopy = new LinkedList(_sessions.values()); + final Iterator it = sessionCopy.iterator(); + JMSException sessionException = null; + while (it.hasNext()) + { + final AMQSession session = (AMQSession) it.next(); + if (cause != null) + { + session.closed(cause); + } + else + { + try + { + if (starttime != -1) + { + timeout = adjustTimeout(timeout, starttime); + } + + session.close(timeout); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e); + sessionException = e; + } + } + } + + _sessions.clear(); + if (sessionException != null) + { + throw sessionException; + } + } + + public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, + ServerSessionPool sessionPool, int maxMessages) throws JMSException + { + checkNotClosed(); + + return null; + } + + public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool, + int maxMessages) throws JMSException + { + checkNotClosed(); + + return null; + } + + public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool, + int maxMessages) throws JMSException + { + checkNotClosed(); + + return null; + } + + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, + ServerSessionPool sessionPool, int maxMessages) throws JMSException + { + // TODO Auto-generated method stub + checkNotClosed(); + + return null; + } + + public long getMaximumChannelCount() throws JMSException + { + checkNotClosed(); + + return _maximumChannelCount; + } + + public void setConnectionListener(ConnectionListener listener) + { + _connectionListener = listener; + } + + public ConnectionListener getConnectionListener() + { + return _connectionListener; + } + + public void setMaximumChannelCount(long maximumChannelCount) + { + _maximumChannelCount = maximumChannelCount; + } + + public void setMaximumFrameSize(long frameMax) + { + _maximumFrameSize = frameMax; + } + + public long getMaximumFrameSize() + { + return _maximumFrameSize; + } + + public ChannelToSessionMap getSessions() + { + return _sessions; + } + + public String getUsername() + { + return _username; + } + + public String getPassword() + { + return _password; + } + + public String getVirtualHost() + { + return _virtualHost; + } + + public AMQProtocolHandler getProtocolHandler() + { + return _protocolHandler; + } + + public boolean started() + { + return _started; + } + + public void bytesSent(long writtenBytes) + { + if (_connectionListener != null) + { + _connectionListener.bytesSent(writtenBytes); + } + } + + public void bytesReceived(long receivedBytes) + { + if (_connectionListener != null) + { + _connectionListener.bytesReceived(receivedBytes); + } + } + + /** + * Fire the preFailover event to the registered connection listener (if any) + * + * @param redirect true if this is the result of a redirect request rather than a connection error + * + * @return true if no listener or listener does not veto change + */ + public boolean firePreFailover(boolean redirect) + { + boolean proceed = true; + if (_connectionListener != null) + { + proceed = _connectionListener.preFailover(redirect); + } + + return proceed; + } + + /** + * Fire the preResubscribe event to the registered connection listener (if any). If the listener vetoes + * resubscription then all the sessions are closed. + * + * @return true if no listener or listener does not veto resubscription. + * + * @throws JMSException + */ + public boolean firePreResubscribe() throws JMSException + { + if (_connectionListener != null) + { + boolean resubscribe = _connectionListener.preResubscribe(); + if (!resubscribe) + { + markAllSessionsClosed(); + } + + return resubscribe; + } + else + { + return true; + } + } + + /** Fires a failover complete event to the registered connection listener (if any). */ + public void fireFailoverComplete() + { + if (_connectionListener != null) + { + _connectionListener.failoverComplete(); + } + } + + /** + * In order to protect the consistency of the connection and its child sessions, consumers and producers, the + * "failover mutex" must be held when doing any operations that could be corrupted during failover. + * + * @return a mutex. Guaranteed never to change for the lifetime of this connection even if failover occurs. + */ + public final Object getFailoverMutex() + { + return _failoverMutex; + } + + public void resubscribeSessions() throws JMSException, AMQException, FailoverException + { + _delegate.resubscribeSessions(); + } + + /** + * If failover is taking place this will block until it has completed. If failover is not taking place it will + * return immediately. + * + * @throws InterruptedException + */ + public void blockUntilNotFailingOver() throws InterruptedException + { + _protocolHandler.blockUntilNotFailingOver(); + } + + /** + * Invoked by the AMQProtocolSession when a protocol session exception has occurred. This method sends the exception + * to a JMS exception listener, if configured, and propagates the exception to sessions, which in turn will + * propagate to consumers. This allows synchronous consumers to have exceptions thrown to them. + * + * @param cause the exception + */ + public void exceptionReceived(Throwable cause) + { + + if (_logger.isDebugEnabled()) + { + _logger.debug("exceptionReceived done by:" + Thread.currentThread().getName(), cause); + } + + final JMSException je; + if (cause instanceof JMSException) + { + je = (JMSException) cause; + } + else + { + AMQConstant code = null; + + if (cause instanceof AMQException) + { + code = ((AMQException) cause).getErrorCode(); + } + + if (code != null) + { + je = + new JMSException(Integer.toString(code.getCode()), "Exception thrown against " + toString() + ": " + + cause); + } + else + { + //Should never get here as all AMQEs are required to have an ErrorCode! + je = new JMSException("Exception thrown against " + toString() + ": " + cause); + } + + if (cause instanceof Exception) + { + je.setLinkedException((Exception) cause); + } + } + + boolean closer = false; + + // in the case of an IOException, MINA has closed the protocol session so we set _closed to true + // so that any generic client code that tries to close the connection will not mess up this error + // handling sequence + if (cause instanceof IOException) + { + closer = !_closed.getAndSet(true); + + _protocolHandler.getProtocolSession().notifyError(je); + } + + if (_exceptionListener != null) + { + _exceptionListener.onException(je); + } + else + { + _logger.error("Throwable Received but no listener set: " + cause.getMessage()); + } + + if (hardError(cause)) + { + try + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing AMQConnection due to :" + cause.getMessage()); + } + + closer = (!_closed.getAndSet(true)) || closer; + if (closer) + { + closeAllSessions(cause, -1, -1); // FIXME: when doing this end up with RejectedExecutionException from executor. + } + } + catch (JMSException e) + { + _logger.error("Error closing all sessions: " + e, e); + } + + } + else + { + _logger.info("Not a hard-error connection not closing: " + cause.getMessage()); + } + } + + private boolean hardError(Throwable cause) + { + if (cause instanceof AMQException) + { + return ((AMQException) cause).isHardError(); + } + + return true; + } + + void registerSession(int channelId, AMQSession session) + { + _sessions.put(channelId, session); + } + + void deregisterSession(int channelId) + { + _sessions.remove(channelId); + } + + public String toString() + { + StringBuffer buf = new StringBuffer("AMQConnection:\n"); + if (_failoverPolicy.getCurrentBrokerDetails() == null) + { + buf.append("No active broker connection"); + } + else + { + BrokerDetails bd = _failoverPolicy.getCurrentBrokerDetails(); + buf.append("Host: ").append(String.valueOf(bd.getHost())); + buf.append("\nPort: ").append(String.valueOf(bd.getPort())); + } + + buf.append("\nVirtual Host: ").append(String.valueOf(_virtualHost)); + buf.append("\nClient ID: ").append(String.valueOf(_clientName)); + buf.append("\nActive session count: ").append((_sessions == null) ? 0 : _sessions.size()); + + return buf.toString(); + } + + public String toURL() + { + return _connectionURL.toString(); + } + + public Reference getReference() throws NamingException + { + return new Reference(AMQConnection.class.getName(), new StringRefAddr(AMQConnection.class.getName(), toURL()), + AMQConnectionFactory.class.getName(), null); // factory location + } + + public SSLConfiguration getSSLConfiguration() + { + return _sslConfiguration; + } + + public AMQShortString getDefaultTopicExchangeName() + { + return _defaultTopicExchangeName; + } + + public void setDefaultTopicExchangeName(AMQShortString defaultTopicExchangeName) + { + _defaultTopicExchangeName = defaultTopicExchangeName; + } + + public AMQShortString getDefaultQueueExchangeName() + { + return _defaultQueueExchangeName; + } + + public void setDefaultQueueExchangeName(AMQShortString defaultQueueExchangeName) + { + _defaultQueueExchangeName = defaultQueueExchangeName; + } + + public AMQShortString getTemporaryTopicExchangeName() + { + return _temporaryTopicExchangeName; + } + + public AMQShortString getTemporaryQueueExchangeName() + { + return _temporaryQueueExchangeName; // To change body of created methods use File | Settings | File Templates. + } + + public void setTemporaryTopicExchangeName(AMQShortString temporaryTopicExchangeName) + { + _temporaryTopicExchangeName = temporaryTopicExchangeName; + } + + public void setTemporaryQueueExchangeName(AMQShortString temporaryQueueExchangeName) + { + _temporaryQueueExchangeName = temporaryQueueExchangeName; + } + + public void performConnectionTask(Runnable task) + { + _taskPool.execute(task); + } + + public AMQSession getSession(int channelId) + { + return _sessions.get(channelId); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolVersion; + } + + public void setProtocolVersion(ProtocolVersion protocolVersion) + { + _protocolVersion = protocolVersion; + _protocolHandler.getProtocolSession().setProtocolVersion(protocolVersion); + } + + public boolean isFailingOver() + { + return (_protocolHandler.getFailoverLatch() != null); + } + + /** + * Get the maximum number of messages that this connection can pre-fetch. + * + * @return The maximum number of messages that this connection can pre-fetch. + */ + public long getMaxPrefetch() + { + return _maxPrefetch; + } + + /** + * Indicates whether persistent messages are synchronized + * + * @return true if persistent messages are synchronized false otherwise + */ + public boolean getSyncPersistence() + { + return _syncPersistence; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java new file mode 100644 index 0000000000..b64147fe8f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate.java @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.io.IOException; + +import javax.jms.JMSException; +import javax.jms.XASession; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.Session; + +public interface AMQConnectionDelegate +{ + ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException; + + Session createSession(final boolean transacted, final int acknowledgeMode, + final int prefetchHigh, final int prefetchLow) throws JMSException; + + XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException; + + void resubscribeSessions() throws JMSException, AMQException, FailoverException; + + void closeConnection(long timeout) throws JMSException, AMQException; + + T executeRetrySupport(FailoverProtectedOperation operation) throws E; + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java new file mode 100644 index 0000000000..30ea4dcf8d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -0,0 +1,269 @@ +package org.apache.qpid.client; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.io.IOException; + +import java.util.ArrayList; +import java.util.List; + +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.XASession; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQProtocolException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.Session; +import org.apache.qpid.ErrorCode; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionClose; +import org.apache.qpid.transport.ConnectionException; +import org.apache.qpid.transport.ConnectionListener; +import org.apache.qpid.transport.ProtocolVersionException; +import org.apache.qpid.transport.TransportException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, ConnectionListener +{ + /** + * This class logger. + */ + private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionDelegate_0_10.class); + + /** + * The AMQ Connection. + */ + private AMQConnection _conn; + + /** + * The QpidConeection instance that is mapped with thie JMS connection. + */ + org.apache.qpid.transport.Connection _qpidConnection; + private ConnectionException exception = null; + + //--- constructor + public AMQConnectionDelegate_0_10(AMQConnection conn) + { + _conn = conn; + _qpidConnection = new Connection(); + _qpidConnection.setConnectionListener(this); + } + + /** + * create a Session and start it if required. + */ + public Session createSession(boolean transacted, int acknowledgeMode, int prefetchHigh, int prefetchLow) + throws JMSException + { + _conn.checkNotClosed(); + int channelId = _conn._idFactory.incrementAndGet(); + AMQSession session; + try + { + session = new AMQSession_0_10(_qpidConnection, _conn, channelId, transacted, acknowledgeMode, prefetchHigh, + prefetchLow); + _conn.registerSession(channelId, session); + if (_conn._started) + { + session.start(); + } + } + catch (Exception e) + { + _logger.error("exception creating session:", e); + throw new JMSAMQException("cannot create session", e); + } + return session; + } + + /** + * create an XA Session and start it if required. + */ + public XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException + { + _conn.checkNotClosed(); + int channelId = _conn._idFactory.incrementAndGet(); + XASessionImpl session; + try + { + session = new XASessionImpl(_qpidConnection, _conn, channelId, prefetchHigh, prefetchLow); + _conn.registerSession(channelId, session); + if (_conn._started) + { + session.start(); + } + } + catch (Exception e) + { + throw new JMSAMQException("cannot create session", e); + } + return session; + } + + + /** + * Make a connection with the broker + * + * @param brokerDetail The detail of the broker to connect to. + * @throws IOException + * @throws AMQException + */ + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + { + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("connecting to host: " + brokerDetail.getHost() + + " port: " + brokerDetail.getPort() + + " vhost: " + _conn.getVirtualHost() + + " username: " + _conn.getUsername() + + " password: " + _conn.getPassword()); + } + _qpidConnection.connect(brokerDetail.getHost(), brokerDetail.getPort(), _conn.getVirtualHost(), + _conn.getUsername(), _conn.getPassword(), brokerDetail.useSSL()); + _conn._connected = true; + } + catch(ProtocolVersionException pe) + { + return new ProtocolVersion(pe.getMajor(), pe.getMinor()); + } + catch (ConnectionException e) + { + throw new AMQException(AMQConstant.CHANNEL_ERROR, "cannot connect to broker", e); + } + + return null; + } + + /** + * Not supported at this level. + */ + public void resubscribeSessions() throws JMSException, AMQException, FailoverException + { + List sessions = new ArrayList(_conn.getSessions().values()); + _logger.info(String.format("Resubscribing sessions = %s sessions.size=%s", sessions, sessions.size())); + for (AMQSession s : sessions) + { + ((AMQSession_0_10) s)._qpidConnection = _qpidConnection; + s.resubscribe(); + } + } + + + public void closeConnection(long timeout) throws JMSException, AMQException + { + try + { + _qpidConnection.close(); + } + catch (TransportException e) + { + throw new AMQException(e.getMessage(), e); + } + } + + public void opened(Connection conn) {} + + public void exception(Connection conn, ConnectionException exc) + { + if (exception != null) + { + _logger.error("previous exception", exception); + } + + exception = exc; + } + + public void closed(Connection conn) + { + ConnectionException exc = exception; + exception = null; + + if (exc == null) + { + return; + } + + ConnectionClose close = exc.getClose(); + if (close == null) + { + try + { + if (_conn.firePreFailover(false) && _conn.attemptReconnection()) + { + _qpidConnection.resume(); + + if (_conn.firePreResubscribe()) + { + _conn.resubscribeSessions(); + } + + _conn.fireFailoverComplete(); + return; + } + } + catch (Exception e) + { + _logger.error("error during failover", e); + } + } + + ExceptionListener listener = _conn._exceptionListener; + if (listener == null) + { + _logger.error("connection exception: " + conn, exc); + } + else + { + String code = null; + if (close != null) + { + code = close.getReplyCode().toString(); + } + + JMSException ex = new JMSException(exc.getMessage(), code); + ex.initCause(exc); + listener.onException(ex); + } + } + + public T executeRetrySupport(FailoverProtectedOperation operation) throws E + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + throw new RuntimeException(e); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java new file mode 100755 index 0000000000..d95e2e3dff --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_9.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + + +public class AMQConnectionDelegate_0_9 extends AMQConnectionDelegate_8_0 +{ + + public AMQConnectionDelegate_0_9(AMQConnection conn) + { + super(conn); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java new file mode 100644 index 0000000000..035e3830ca --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_8_0.java @@ -0,0 +1,287 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.io.IOException; +import java.net.ConnectException; +import java.nio.channels.UnresolvedAddressException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; + +import javax.jms.JMSException; +import javax.jms.XASession; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.StateWaiter; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.BasicQosBody; +import org.apache.qpid.framing.BasicQosOkBody; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ChannelOpenOkBody; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.TxSelectBody; +import org.apache.qpid.framing.TxSelectOkBody; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ChannelLimitReachedException; +import org.apache.qpid.transport.network.io.IoTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQConnectionDelegate_8_0 implements AMQConnectionDelegate +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionDelegate_8_0.class); + private AMQConnection _conn; + + + public void closeConnection(long timeout) throws JMSException, AMQException + { + _conn.getProtocolHandler().closeConnection(timeout); + + } + + public AMQConnectionDelegate_8_0(AMQConnection conn) + { + _conn = conn; + } + + protected boolean checkException(Throwable thrown) + { + Throwable cause = thrown.getCause(); + + if (cause == null) + { + cause = thrown; + } + + return ((cause instanceof ConnectException) || (cause instanceof UnresolvedAddressException)); + } + + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws AMQException, IOException + { + final Set openOrClosedStates = + EnumSet.of(AMQState.CONNECTION_OPEN, AMQState.CONNECTION_CLOSED); + + + StateWaiter waiter = _conn._protocolHandler.createWaiter(openOrClosedStates); + + // TODO: use system property thingy for this + if (System.getProperty("UseTransportIo", "false").equals("false")) + { + TransportConnection.getInstance(brokerDetail).connect(_conn._protocolHandler, brokerDetail); + } + else + { + _conn.getProtocolHandler().createIoTransportSession(brokerDetail); + } + + // this blocks until the connection has been set up or when an error + // has prevented the connection being set up + + AMQState state = waiter.await(); + + if(state == AMQState.CONNECTION_OPEN) + { + _conn._failoverPolicy.attainedConnection(); + _conn._connected = true; + } + + return null; + } + + public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch) + throws JMSException + { + return createSession(transacted, acknowledgeMode, prefetch, prefetch); + } + + public XASession createXASession(int prefetchHigh, int prefetchLow) throws JMSException + { + throw new UnsupportedOperationException("0_8 version does not provide XA support"); + } + + public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, + final int prefetchHigh, final int prefetchLow) throws JMSException + { + _conn.checkNotClosed(); + + if (_conn.channelLimitReached()) + { + throw new ChannelLimitReachedException(_conn._maximumChannelCount); + } + + return new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException + { + int channelId = _conn._idFactory.incrementAndGet(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Write channel open frame for channel id " + channelId); + } + + // We must create the session and register it before actually sending the frame to the server to + // open it, so that there is no window where we could receive data on the channel and not be set + // up to handle it appropriately. + AMQSession session = + new AMQSession_0_8(_conn, channelId, transacted, acknowledgeMode, prefetchHigh, + prefetchLow); + // _protocolHandler.addSessionByChannel(channelId, session); + _conn.registerSession(channelId, session); + + boolean success = false; + try + { + createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); + success = true; + } + catch (AMQException e) + { + JMSException jmse = new JMSException("Error creating session: " + e); + jmse.setLinkedException(e); + throw jmse; + } + finally + { + if (!success) + { + _conn.deregisterSession(channelId); + } + } + + if (_conn._started) + { + try + { + session.start(); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + } + + return session; + } + }, _conn).execute(); + } + + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + ChannelOpenBody channelOpenBody = _conn.getProtocolHandler().getMethodRegistry().createChannelOpenBody(null); + // TODO: Be aware of possible changes to parameter order as versions change. + _conn._protocolHandler.syncWrite(channelOpenBody.generateFrame(channelId), ChannelOpenOkBody.class); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + BasicQosBody basicQosBody = _conn.getProtocolHandler().getMethodRegistry().createBasicQosBody(0,prefetchHigh,false); + _conn._protocolHandler.syncWrite(basicQosBody.generateFrame(channelId),BasicQosOkBody.class); + + if (transacted) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Issuing TxSelect for " + channelId); + } + TxSelectBody body = _conn.getProtocolHandler().getMethodRegistry().createTxSelectBody(); + + // TODO: Be aware of possible changes to parameter order as versions change. + _conn._protocolHandler.syncWrite(body.generateFrame(channelId), TxSelectOkBody.class); + } + } + + /** + * For all sessions, and for all consumers in those sessions, resubscribe. This is called during failover handling. + * The caller must hold the failover mutex before calling this method. + */ + public void resubscribeSessions() throws JMSException, AMQException, FailoverException + { + ArrayList sessions = new ArrayList(_conn.getSessions().values()); + _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey? + for (Iterator it = sessions.iterator(); it.hasNext();) + { + AMQSession s = (AMQSession) it.next(); + // _protocolHandler.addSessionByChannel(s.getChannelId(), s); + reopenChannel(s.getChannelId(), s.getDefaultPrefetchHigh(), s.getDefaultPrefetchLow(), s.getTransacted()); + s.resubscribe(); + } + } + + private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + try + { + createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); + } + catch (AMQException e) + { + _conn.deregisterSession(channelId); + throw new AMQException(null, "Error reopening channel " + channelId + " after failover: " + e, e); + } + } + + public T executeRetrySupport(FailoverProtectedOperation operation) throws E + { + while (true) + { + try + { + _conn.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + _logger.debug("Interrupted: " + e, e); + + return null; + } + + synchronized (_conn.getFailoverMutex()) + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + _logger.debug("Failover exception caught during operation: " + e, e); + } + catch (IllegalStateException e) + { + if (!(e.getMessage().startsWith("Fail-over interupted no-op failover support"))) + { + throw e; + } + } + } + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java new file mode 100644 index 0000000000..01a915f2cc --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java @@ -0,0 +1,552 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Hashtable; +import java.util.UUID; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import javax.naming.spi.ObjectFactory; + +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.URLSyntaxException; + + +public class AMQConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, + ObjectFactory, Referenceable, XATopicConnectionFactory, + XAQueueConnectionFactory, XAConnectionFactory +{ + private String _host; + private int _port; + private String _defaultUsername; + private String _defaultPassword; + private String _virtualPath; + + private ConnectionURL _connectionDetails; + private SSLConfiguration _sslConfig; + + public AMQConnectionFactory() + { + } + + /** + * This is the Only constructor used! + * It is used form the context and from the JNDI objects. + */ + public AMQConnectionFactory(String url) throws URLSyntaxException + { + _connectionDetails = new AMQConnectionURL(url); + } + + /** + * This constructor is never used! + */ + public AMQConnectionFactory(ConnectionURL url) + { + _connectionDetails = url; + } + + /** + * This constructor is never used! + */ + public AMQConnectionFactory(String broker, String username, String password, String clientName, String virtualHost) + throws URLSyntaxException + { + this(new AMQConnectionURL( + ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" + clientName + "/" + virtualHost + "?brokerlist='" + broker + "'")); + } + + /** + * This constructor is never used! + */ + public AMQConnectionFactory(String host, int port, String virtualPath) + { + this(host, port, "guest", "guest", virtualPath); + } + + /** + * This constructor is never used! + */ + public AMQConnectionFactory(String host, int port, String defaultUsername, String defaultPassword, + String virtualPath) + { + _host = host; + _port = port; + _defaultUsername = defaultUsername; + _defaultPassword = defaultPassword; + _virtualPath = virtualPath; + +//todo when setting Host/Port has been resolved then we can use this otherwise those methods won't work with the following line. +// _connectionDetails = new AMQConnectionURL( +// ConnectionURL.AMQ_PROTOCOL + "://" + +// _defaultUsername + ":" + _defaultPassword + "@" + +// virtualPath + "?brokerlist='tcp://" + host + ":" + port + "'"); + } + + /** + * @return The _defaultPassword. + */ + public final String getDefaultPassword(String password) + { + if (_connectionDetails != null) + { + return _connectionDetails.getPassword(); + } + else + { + return _defaultPassword; + } + } + + /** + * @param password The _defaultPassword to set. + */ + public final void setDefaultPassword(String password) + { + if (_connectionDetails != null) + { + _connectionDetails.setPassword(password); + } + _defaultPassword = password; + } + + /** + * Getter for SSLConfiguration + * + * @return SSLConfiguration if set, otherwise null + */ + public final SSLConfiguration getSSLConfiguration() + { + return _sslConfig; + } + + /** + * Setter for SSLConfiguration + * + * @param sslConfig config to store + */ + public final void setSSLConfiguration(SSLConfiguration sslConfig) + { + _sslConfig = sslConfig; + } + + /** + * @return The _defaultPassword. + */ + public final String getDefaultUsername(String password) + { + if (_connectionDetails != null) + { + return _connectionDetails.getUsername(); + } + else + { + return _defaultUsername; + } + } + + /** + * @param username The _defaultUsername to set. + */ + public final void setDefaultUsername(String username) + { + if (_connectionDetails != null) + { + _connectionDetails.setUsername(username); + } + _defaultUsername = username; + } + + /** + * @return The _host . + */ + public final String getHost() + { + //todo this doesn't make sense in a multi broker URL as we have no current as that is done by AMQConnection + return _host; + } + + /** + * @param host The _host to set. + */ + public final void setHost(String host) + { + //todo if _connectionDetails is set then run _connectionDetails.addBrokerDetails() + // Should perhaps have this method changed to setBroker(host,port) + _host = host; + } + + /** + * @return _port The _port to set. + */ + public final int getPort() + { + //todo see getHost + return _port; + } + + /** + * @param port The port to set. + */ + public final void setPort(int port) + { + //todo see setHost + _port = port; + } + + /** + * @return he _virtualPath. + */ + public final String getVirtualPath() + { + if (_connectionDetails != null) + { + return _connectionDetails.getVirtualHost(); + } + else + { + return _virtualPath; + } + } + + /** + * @param path The _virtualPath to set. + */ + public final void setVirtualPath(String path) + { + if (_connectionDetails != null) + { + _connectionDetails.setVirtualHost(path); + } + + _virtualPath = path; + } + + public static String getUniqueClientID() + { + try + { + InetAddress addr = InetAddress.getLocalHost(); + return addr.getHostName() + System.currentTimeMillis(); + } + catch (UnknownHostException e) + { + return "UnknownHost" + UUID.randomUUID(); + } + } + + public Connection createConnection() throws JMSException + { + try + { + if (_connectionDetails != null) + { + if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + { + _connectionDetails.setClientName(getUniqueClientID()); + } + return new AMQConnection(_connectionDetails, _sslConfig); + } + else + { + return new AMQConnection(_host, _port, _defaultUsername, _defaultPassword, getUniqueClientID(), + _virtualPath); + } + } + catch (Exception e) + { + JMSException jmse = new JMSException("Error creating connection: " + e.getMessage()); + jmse.setLinkedException(e); + throw jmse; + } + + + } + + public Connection createConnection(String userName, String password) throws JMSException + { + return createConnection(userName, password, null); + } + + public Connection createConnection(String userName, String password, String id) throws JMSException + { + try + { + if (_connectionDetails != null) + { + _connectionDetails.setUsername(userName); + _connectionDetails.setPassword(password); + + if (id != null && !id.equals("")) + { + _connectionDetails.setClientName(id); + } + else if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + { + _connectionDetails.setClientName(getUniqueClientID()); + } + return new AMQConnection(_connectionDetails, _sslConfig); + } + else + { + return new AMQConnection(_host, _port, userName, password, (id != null ? id : getUniqueClientID()), _virtualPath); + } + } + catch (Exception e) + { + JMSException jmse = new JMSException("Error creating connection: " + e.getMessage()); + jmse.setLinkedException(e); + throw jmse; + } + } + + public QueueConnection createQueueConnection() throws JMSException + { + return (QueueConnection) createConnection(); + } + + public QueueConnection createQueueConnection(String username, String password) throws JMSException + { + return (QueueConnection) createConnection(username, password); + } + + public TopicConnection createTopicConnection() throws JMSException + { + return (TopicConnection) createConnection(); + } + + public TopicConnection createTopicConnection(String username, String password) throws JMSException + { + return (TopicConnection) createConnection(username, password); + } + + + public ConnectionURL getConnectionURL() + { + return _connectionDetails; + } + + /** + * JNDI interface to create objects from References. + * + * @param obj The Reference from JNDI + * @param name + * @param ctx + * @param env + * + * @return AMQConnection,AMQTopic,AMQQueue, or AMQConnectionFactory. + * + * @throws Exception + */ + public Object getObjectInstance(Object obj, Name name, Context ctx, Hashtable env) throws Exception + { + if (obj instanceof Reference) + { + Reference ref = (Reference) obj; + + if (ref.getClassName().equals(AMQConnection.class.getName())) + { + RefAddr addr = ref.get(AMQConnection.class.getName()); + + if (addr != null) + { + return new AMQConnection((String) addr.getContent()); + } + } + + if (ref.getClassName().equals(AMQQueue.class.getName())) + { + RefAddr addr = ref.get(AMQQueue.class.getName()); + + if (addr != null) + { + return new AMQQueue(new AMQBindingURL((String) addr.getContent())); + } + } + + if (ref.getClassName().equals(AMQTopic.class.getName())) + { + RefAddr addr = ref.get(AMQTopic.class.getName()); + + if (addr != null) + { + return new AMQTopic(new AMQBindingURL((String) addr.getContent())); + } + } + + if (ref.getClassName().equals(AMQConnectionFactory.class.getName())) + { + RefAddr addr = ref.get(AMQConnectionFactory.class.getName()); + + if (addr != null) + { + return new AMQConnectionFactory((String) addr.getContent()); + } + } + + } + return null; + } + + + public Reference getReference() throws NamingException + { + return new Reference( + AMQConnectionFactory.class.getName(), + new StringRefAddr(AMQConnectionFactory.class.getName(), _connectionDetails.getURL()), + AMQConnectionFactory.class.getName(), null); // factory location + } + + // --------------------------------------------------------------------------------------------------- + // the following methods are provided for XA compatibility + // Those methods are only supported by 0_10 and above + // --------------------------------------------------------------------------------------------------- + + /** + * Creates a XAConnection with the default user identity. + *

The XAConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @return A newly created XAConnection + * @throws JMSException If creating the XAConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XAConnection createXAConnection() throws JMSException + { + try + { + return new XAConnectionImpl(_connectionDetails, _sslConfig); + } + catch (Exception e) + { + JMSException jmse = new JMSException("Error creating connection: " + e.getMessage()); + jmse.setLinkedException(e); + throw jmse; + } + } + + /** + * Creates a XAConnection with the specified user identity. + *

The XAConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @param username the caller's user name + * @param password the caller's password + * @return A newly created XAConnection. + * @throws JMSException If creating the XAConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XAConnection createXAConnection(String username, String password) throws JMSException + { + if (_connectionDetails != null) + { + _connectionDetails.setUsername(username); + _connectionDetails.setPassword(password); + + if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + { + _connectionDetails.setClientName(getUniqueClientID()); + } + } + else + { + throw new JMSException("A URL must be specified to access XA connections"); + } + return createXAConnection(); + } + + + /** + * Creates a XATopicConnection with the default user identity. + *

The XATopicConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @return A newly created XATopicConnection + * @throws JMSException If creating the XATopicConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XATopicConnection createXATopicConnection() throws JMSException + { + return (XATopicConnection) createXAConnection(); + } + + /** + * Creates a XATopicConnection with the specified user identity. + *

The XATopicConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @param username the caller's user name + * @param password the caller's password + * @return A newly created XATopicConnection. + * @throws JMSException If creating the XATopicConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XATopicConnection createXATopicConnection(String username, String password) throws JMSException + { + return (XATopicConnection) createXAConnection(username, password); + } + + /** + * Creates a XAQueueConnection with the default user identity. + *

The XAQueueConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @return A newly created XAQueueConnection + * @throws JMSException If creating the XAQueueConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XAQueueConnection createXAQueueConnection() throws JMSException + { + return (XAQueueConnection) createXAConnection(); + } + + /** + * Creates a XAQueueConnection with the specified user identity. + *

The XAQueueConnection is created in stopped mode. No messages + * will be delivered until the Connection.start method + * is explicitly called. + * + * @param username the caller's user name + * @param password the caller's password + * @return A newly created XAQueueConnection. + * @throws JMSException If creating the XAQueueConnection fails due to some internal error. + * @throws JMSSecurityException If client authentication fails due to an invalid user name or password. + */ + public XAQueueConnection createXAQueueConnection(String username, String password) throws JMSException + { + return (XAQueueConnection) createXAConnection(username, password); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java new file mode 100644 index 0000000000..60e4a5960a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java @@ -0,0 +1,294 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.client.url.URLParser; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.URLHelper; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQConnectionURL implements ConnectionURL +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionURL.class); + + private String _url; + private String _failoverMethod; + private Map _failoverOptions; + private Map _options; + private List _brokers; + private String _clientName; + private String _username; + private String _password; + private String _virtualHost; + private AMQShortString _defaultQueueExchangeName; + private AMQShortString _defaultTopicExchangeName; + private AMQShortString _temporaryTopicExchangeName; + private AMQShortString _temporaryQueueExchangeName; + + public AMQConnectionURL(String fullURL) throws URLSyntaxException + { + if (fullURL == null) throw new IllegalArgumentException("URL cannot be null"); + _url = fullURL; + _options = new HashMap(); + _brokers = new LinkedList(); + _failoverOptions = new HashMap(); + new URLParser(this); + } + + public String getURL() + { + return _url; + } + + public Map getOptions() + { + return _options; + } + + public String getFailoverMethod() + { + return _failoverMethod; + } + + public void setFailoverMethod(String failoverMethod) + { + _failoverMethod = failoverMethod; + } + + public Map getFailoverOptions() + { + return _failoverOptions; + } + + public String getFailoverOption(String key) + { + return _failoverOptions.get(key); + } + + public void setFailoverOption(String key, String value) + { + _failoverOptions.put(key, value); + } + + public int getBrokerCount() + { + return _brokers.size(); + } + + public BrokerDetails getBrokerDetails(int index) + { + if (index < _brokers.size()) + { + return _brokers.get(index); + } + else + { + return null; + } + } + + public void addBrokerDetails(BrokerDetails broker) + { + if (!(_brokers.contains(broker))) + { + _brokers.add(broker); + } + } + + public void setBrokerDetails(List brokers) + { + _brokers = brokers; + } + + public List getAllBrokerDetails() + { + return _brokers; + } + + public String getClientName() + { + return _clientName; + } + + public void setClientName(String clientName) + { + _clientName = clientName; + } + + public String getUsername() + { + return _username; + } + + public void setUsername(String username) + { + _username = username; + } + + public String getPassword() + { + return _password; + } + + public void setPassword(String password) + { + _password = password; + } + + public String getVirtualHost() + { + return _virtualHost; + } + + public void setVirtualHost(String virtuaHost) + { + _virtualHost = virtuaHost; + } + + public String getOption(String key) + { + return _options.get(key); + } + + public void setOption(String key, String value) + { + _options.put(key, value); + } + + public AMQShortString getDefaultQueueExchangeName() + { + return _defaultQueueExchangeName; + } + + public void setDefaultQueueExchangeName(AMQShortString defaultQueueExchangeName) + { + _defaultQueueExchangeName = defaultQueueExchangeName; + } + + public AMQShortString getDefaultTopicExchangeName() + { + return _defaultTopicExchangeName; + } + + public void setDefaultTopicExchangeName(AMQShortString defaultTopicExchangeName) + { + _defaultTopicExchangeName = defaultTopicExchangeName; + } + + public AMQShortString getTemporaryQueueExchangeName() + { + return _temporaryQueueExchangeName; + } + + public void setTemporaryQueueExchangeName(AMQShortString temporaryQueueExchangeName) + { + _temporaryQueueExchangeName = temporaryQueueExchangeName; + } + + public AMQShortString getTemporaryTopicExchangeName() + { + return _temporaryTopicExchangeName; + } + + public void setTemporaryTopicExchangeName(AMQShortString temporaryTopicExchangeName) + { + _temporaryTopicExchangeName = temporaryTopicExchangeName; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + + sb.append(AMQ_PROTOCOL); + sb.append("://"); + + if (_username != null) + { + sb.append(_username); + + if (_password != null) + { + sb.append(':'); + sb.append("********"); + } + + sb.append('@'); + } + + sb.append(_clientName); + + sb.append(_virtualHost); + + sb.append(optionsToString()); + + return sb.toString(); + } + + private String optionsToString() + { + StringBuffer sb = new StringBuffer(); + + sb.append("?" + OPTIONS_BROKERLIST + "='"); + + for (BrokerDetails service : _brokers) + { + sb.append(service.toString()); + sb.append(';'); + } + + sb.deleteCharAt(sb.length() - 1); + sb.append("'"); + + if (_failoverMethod != null) + { + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + sb.append(OPTIONS_FAILOVER + "='"); + sb.append(_failoverMethod); + sb.append(URLHelper.printOptions(_failoverOptions)); + sb.append("'"); + } + + return sb.toString(); + } + + public static void main(String[] args) throws URLSyntaxException + { + String url2 = + "amqp://ritchiem:bob@temp/testHost?brokerlist='tcp://localhost:5672;tcp://fancyserver:3000/',failover='roundrobin'"; + // "amqp://user:pass@clientid/virtualhost?brokerlist='tcp://host:1?option1=\'value\',option2=\'value\';vm://:3?option1=\'value\'',failover='method?option1=\'value\',option2='value''"; + + ConnectionURL connectionurl2 = new AMQConnectionURL(url2); + + System.out.println(url2); + System.out.println(connectionurl2); + + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java new file mode 100644 index 0000000000..750599b350 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -0,0 +1,507 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.net.URISyntaxException; + +import javax.jms.Destination; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.URLHelper; + + +public abstract class AMQDestination implements Destination, Referenceable +{ + protected final AMQShortString _exchangeName; + + protected final AMQShortString _exchangeClass; + + protected final boolean _isDurable; + + protected final boolean _isExclusive; + + protected final boolean _isAutoDelete; + + private AMQShortString _queueName; + + private AMQShortString _routingKey; + + private AMQShortString[] _bindingKeys; + + private String _url; + private AMQShortString _urlAsShortString; + + private boolean _checkedForQueueBinding; + + private boolean _exchangeExistsChecked; + + private byte[] _byteEncoding; + private static final int IS_DURABLE_MASK = 0x1; + private static final int IS_EXCLUSIVE_MASK = 0x2; + private static final int IS_AUTODELETE_MASK = 0x4; + + public static final int QUEUE_TYPE = 1; + public static final int TOPIC_TYPE = 2; + public static final int UNKNOWN_TYPE = 3; + + protected AMQDestination(String url) throws URISyntaxException + { + this(new AMQBindingURL(url)); + } + + protected AMQDestination(BindingURL binding) + { + _exchangeName = binding.getExchangeName(); + _exchangeClass = binding.getExchangeClass(); + + _isExclusive = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_EXCLUSIVE)); + _isAutoDelete = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_AUTODELETE)); + _isDurable = Boolean.parseBoolean(binding.getOption(BindingURL.OPTION_DURABLE)); + _queueName = binding.getQueueName() == null ? null : binding.getQueueName(); + _routingKey = binding.getRoutingKey() == null ? null : binding.getRoutingKey(); + _bindingKeys = binding.getBindingKeys() == null || binding.getBindingKeys().length == 0 ? new AMQShortString[0] : binding.getBindingKeys(); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, AMQShortString queueName) + { + this(exchangeName, exchangeClass, routingKey, false, false, queueName, null); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, AMQShortString queueName, AMQShortString[] bindingKeys) + { + this(exchangeName, exchangeClass, routingKey, false, false, queueName,bindingKeys); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName) + { + this(exchangeName, exchangeClass, destinationName, false, false, null,null); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName) + { + this(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, false,null); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName,AMQShortString[] bindingKeys) + { + this(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, false,bindingKeys); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, boolean isDurable){ + this (exchangeName, exchangeClass, routingKey, isExclusive,isAutoDelete,queueName,isDurable,null); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, boolean isDurable,AMQShortString[] bindingKeys) + { + // If used with a fannout exchange, the routing key can be null + if ( !ExchangeDefaults.FANOUT_EXCHANGE_CLASS.equals(exchangeClass) && routingKey == null) + { + throw new IllegalArgumentException("routingKey exchange must not be null"); + } + if (exchangeName == null) + { + throw new IllegalArgumentException("Exchange name must not be null"); + } + if (exchangeClass == null) + { + throw new IllegalArgumentException("Exchange class must not be null"); + } + _exchangeName = exchangeName; + _exchangeClass = exchangeClass; + _routingKey = routingKey; + _isExclusive = isExclusive; + _isAutoDelete = isAutoDelete; + _queueName = queueName; + _isDurable = isDurable; + _bindingKeys = bindingKeys == null || bindingKeys.length == 0 ? new AMQShortString[0] : bindingKeys; + } + + public AMQShortString getEncodedName() + { + if(_urlAsShortString == null) + { + toURL(); + } + return _urlAsShortString; + } + + public boolean isDurable() + { + return _isDurable; + } + + public AMQShortString getExchangeName() + { + return _exchangeName; + } + + public AMQShortString getExchangeClass() + { + return _exchangeClass; + } + + public boolean isTopic() + { + return ExchangeDefaults.TOPIC_EXCHANGE_CLASS.equals(_exchangeClass); + } + + public boolean isQueue() + { + return ExchangeDefaults.DIRECT_EXCHANGE_CLASS.equals(_exchangeClass); + } + + public String getQueueName() + { + return _queueName == null ? null : _queueName.toString(); + } + + public AMQShortString getAMQQueueName() + { + return _queueName; + } + + public void setQueueName(AMQShortString queueName) + { + + _queueName = queueName; + // calculated URL now out of date + _url = null; + _urlAsShortString = null; + _byteEncoding = null; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + + public AMQShortString[] getBindingKeys() + { + if (_bindingKeys != null && _bindingKeys.length > 0) + { + return _bindingKeys; + } + else + { + // catering to the common use case where the + //routingKey is the same as the bindingKey. + return new AMQShortString[]{_routingKey}; + } + } + + public boolean isExclusive() + { + return _isExclusive; + } + + public boolean isAutoDelete() + { + return _isAutoDelete; + } + + public abstract boolean isNameRequired(); + + public String toString() + { + return toURL(); + + } + + public boolean isCheckedForQueueBinding() + { + return _checkedForQueueBinding; + } + + public void setCheckedForQueueBinding(boolean checkedForQueueBinding) + { + _checkedForQueueBinding = checkedForQueueBinding; + } + + + public boolean isExchangeExistsChecked() + { + return _exchangeExistsChecked; + } + + public void setExchangeExistsChecked(final boolean exchangeExistsChecked) + { + _exchangeExistsChecked = exchangeExistsChecked; + } + + public String toURL() + { + String url = _url; + if(url == null) + { + + + StringBuffer sb = new StringBuffer(); + + sb.append(_exchangeClass); + sb.append("://"); + sb.append(_exchangeName); + + sb.append("/"+_routingKey+"/"); + + if (_queueName != null) + { + sb.append(_queueName); + } + + sb.append('?'); + + if (_routingKey != null) + { + sb.append(BindingURL.OPTION_ROUTING_KEY); + sb.append("='"); + sb.append(_routingKey).append("'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + + // We can't allow both routingKey and bindingKey + if (_routingKey == null && _bindingKeys != null && _bindingKeys.length>0) + { + + for (AMQShortString bindingKey:_bindingKeys) + { + sb.append(BindingURL.OPTION_BINDING_KEY); + sb.append("='"); + sb.append(bindingKey); + sb.append("'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + + } + } + + if (_isDurable) + { + sb.append(BindingURL.OPTION_DURABLE); + sb.append("='true'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + + if (_isExclusive) + { + sb.append(BindingURL.OPTION_EXCLUSIVE); + sb.append("='true'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + + if (_isAutoDelete) + { + sb.append(BindingURL.OPTION_AUTODELETE); + sb.append("='true'"); + sb.append(URLHelper.DEFAULT_OPTION_SEPERATOR); + } + + //removeKey the last char '?' if there is no options , ',' if there are. + sb.deleteCharAt(sb.length() - 1); + url = sb.toString(); + _url = url; + _urlAsShortString = new AMQShortString(url); + } + return url; + } + + public byte[] toByteEncoding() + { + byte[] encoding = _byteEncoding; + if(encoding == null) + { + int size = _exchangeClass.length() + 1 + + _exchangeName.length() + 1 + + 0 + // in place of the destination name + (_queueName == null ? 0 : _queueName.length()) + 1 + + 1; + encoding = new byte[size]; + int pos = 0; + + pos = _exchangeClass.writeToByteArray(encoding, pos); + pos = _exchangeName.writeToByteArray(encoding, pos); + + encoding[pos++] = (byte)0; + + if(_queueName == null) + { + encoding[pos++] = (byte)0; + } + else + { + pos = _queueName.writeToByteArray(encoding,pos); + } + byte options = 0; + if(_isDurable) + { + options |= IS_DURABLE_MASK; + } + if(_isExclusive) + { + options |= IS_EXCLUSIVE_MASK; + } + if(_isAutoDelete) + { + options |= IS_AUTODELETE_MASK; + } + encoding[pos] = options; + + + _byteEncoding = encoding; + + } + return encoding; + } + + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final AMQDestination that = (AMQDestination) o; + + if (!_exchangeClass.equals(that._exchangeClass)) + { + return false; + } + if (!_exchangeName.equals(that._exchangeName)) + { + return false; + } + if ((_queueName == null && that._queueName != null) || + (_queueName != null && !_queueName.equals(that._queueName))) + { + return false; + } + + return true; + } + + public int hashCode() + { + int result; + result = _exchangeName.hashCode(); + result = 29 * result + _exchangeClass.hashCode(); + //result = 29 * result + _destinationName.hashCode(); + if (_queueName != null) + { + result = 29 * result + _queueName.hashCode(); + } + + return result; + } + + public Reference getReference() throws NamingException + { + return new Reference( + this.getClass().getName(), + new StringRefAddr(this.getClass().getName(), toURL()), + AMQConnectionFactory.class.getName(), + null); // factory location + } + + + public static Destination createDestination(byte[] byteEncodedDestination) + { + AMQShortString exchangeClass; + AMQShortString exchangeName; + AMQShortString routingKey; + AMQShortString queueName; + boolean isDurable; + boolean isExclusive; + boolean isAutoDelete; + + int pos = 0; + exchangeClass = AMQShortString.readFromByteArray(byteEncodedDestination, pos); + pos+= exchangeClass.length() + 1; + exchangeName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); + pos+= exchangeName.length() + 1; + routingKey = AMQShortString.readFromByteArray(byteEncodedDestination, pos); + pos+= (routingKey == null ? 0 : routingKey.length()) + 1; + queueName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); + pos+= (queueName == null ? 0 : queueName.length()) + 1; + int options = byteEncodedDestination[pos]; + isDurable = (options & IS_DURABLE_MASK) != 0; + isExclusive = (options & IS_EXCLUSIVE_MASK) != 0; + isAutoDelete = (options & IS_AUTODELETE_MASK) != 0; + + if (exchangeClass.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) + { + return new AMQQueue(exchangeName,routingKey,queueName,isExclusive,isAutoDelete,isDurable); + } + else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) + { + return new AMQTopic(exchangeName,routingKey,isAutoDelete,queueName,isDurable); + } + else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS)) + { + return new AMQHeadersExchange(routingKey); + } + else + { + throw new IllegalArgumentException("Unknown Exchange Class:" + exchangeClass); + } + + + + } + + public static Destination createDestination(BindingURL binding) + { + AMQShortString type = binding.getExchangeClass(); + + if (type.equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)) + { + return new AMQQueue(binding); + } + else if (type.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) + { + return new AMQTopic(binding); + } + else if (type.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS)) + { + return new AMQHeadersExchange(binding); + } + else if (type.equals(ExchangeDefaults.FANOUT_EXCHANGE_CLASS)) + { + return new AMQQueue(binding); + } + else + { + throw new IllegalArgumentException("Unknown Exchange Class:" + type + " in binding:" + binding); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java new file mode 100644 index 0000000000..b9e9a33cd6 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.BindingURL; + +/** + * A destination backed by a headers exchange + */ +public class AMQHeadersExchange extends AMQDestination +{ + public AMQHeadersExchange(BindingURL binding) + { + this(binding.getExchangeName()); + } + + public AMQHeadersExchange(String name) + { + this(new AMQShortString(name)); + } + + public AMQHeadersExchange(AMQShortString queueName) + { + super(queueName, ExchangeDefaults.HEADERS_EXCHANGE_CLASS, queueName, true, true, null); + } + + public boolean isNameRequired() + { + //Not sure what the best approach is here, probably to treat this like a topic + //and allow server to generate names. As it is AMQ specific it doesn't need to + //fit the JMS API expectations so this is not as yet critical. + return getAMQQueueName() == null; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java new file mode 100644 index 0000000000..08867b5de7 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.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.client; + +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQNoConsumersException indicates failure to pass an immediate message to a consumer. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to pass an immediate message to a consumer. + *
+ */ +public class AMQNoConsumersException extends AMQUndeliveredException +{ + public AMQNoConsumersException(String msg, Object bounced, Throwable cause) + { + super(AMQConstant.NO_CONSUMERS, msg, bounced, cause); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java new file mode 100644 index 0000000000..42ed9c3df7 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.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.client; + +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQNoRouteException indicates that a mandatory message could not be routed. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to route a mandatory message. + *
+ */ +public class AMQNoRouteException extends AMQUndeliveredException +{ + public AMQNoRouteException(String msg, Object bounced, Throwable cause) + { + super(AMQConstant.NO_ROUTE, msg, bounced, cause); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java new file mode 100644 index 0000000000..78b01add14 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java @@ -0,0 +1,165 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.Queue; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.BindingURL; + +public class AMQQueue extends AMQDestination implements Queue +{ + + /** + * Create a reference to a non temporary queue using a BindingURL object. + * Note this does not actually imply the queue exists. + * @param binding a BindingURL object + */ + public AMQQueue(BindingURL binding) + { + super(binding); + } + + /** + * Create a reference to a non temporary queue. Note this does not actually imply the queue exists. + * @param name the name of the queue + */ + public AMQQueue(AMQShortString exchangeName, String name) + { + this(exchangeName, new AMQShortString(name)); + } + + + /** + * Create a reference to a non temporary queue. Note this does not actually imply the queue exists. + * @param name the name of the queue + */ + public AMQQueue(AMQShortString exchangeName, AMQShortString name) + { + this(exchangeName, name, false); + } + + public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName) + { + super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, routingKey, false, + false, queueName, false); + } + + public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName,AMQShortString[] bindingKeys) + { + super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, routingKey, false, + false, queueName, false,bindingKeys); + } + + /** + * Create a reference to a non temporary queue. Note this does not actually imply the queue exists. + * @param name the name of the queue + */ + public AMQQueue(String exchangeName, String name) + { + this(new AMQShortString(exchangeName), new AMQShortString(name), false); + } + + + public AMQQueue(AMQConnection connection, String name) + { + this(connection.getDefaultQueueExchangeName(),name); + } + + public AMQQueue(AMQConnection connection, String name, boolean temporary) + { + this(connection.getDefaultQueueExchangeName(), new AMQShortString(name),temporary); + } + + + /** + * Create a queue with a specified name. + * + * @param name the destination name (used in the routing key) + * @param temporary if true the broker will generate a queue name, also if true then the queue is autodeleted + * and exclusive + */ + public AMQQueue(String exchangeName, String name, boolean temporary) + { + this(new AMQShortString(exchangeName), new AMQShortString(name),temporary); + } + + + /** + * Create a queue with a specified name. + * + * @param name the destination name (used in the routing key) + * @param temporary if true the broker will generate a queue name, also if true then the queue is autodeleted + * and exclusive + */ + public AMQQueue(AMQShortString exchangeName, AMQShortString name, boolean temporary) + { + // queue name is set to null indicating that the broker assigns a name in the case of temporary queues + // temporary queues are typically used as response queues + this(exchangeName, name, temporary?null:name, temporary, temporary, !temporary); + + } + + /** + * Create a reference to a queue. Note this does not actually imply the queue exists. + * @param exchangeName the exchange name we want to send the message to + * @param routingKey the routing key + * @param queueName the queue name + * @param exclusive true if the queue should only permit a single consumer + * @param autoDelete true if the queue should be deleted automatically when the last consumers detaches + */ + public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName, boolean exclusive, boolean autoDelete) + { + this(exchangeName, routingKey, queueName, exclusive, autoDelete, false); + } + + public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName, boolean exclusive, boolean autoDelete, boolean durable) + { + this(exchangeName,routingKey,queueName,exclusive,autoDelete,durable,null); + } + + public AMQQueue(AMQShortString exchangeName, AMQShortString routingKey, AMQShortString queueName, boolean exclusive, boolean autoDelete, boolean durable,AMQShortString[] bindingKeys) + { + super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, routingKey, exclusive, + autoDelete, queueName, durable, bindingKeys); + } + + public AMQShortString getRoutingKey() + { + //return getAMQQueueName(); + if (getAMQQueueName() != null && getAMQQueueName().equals(super.getRoutingKey())) + { + return getAMQQueueName(); + } + else + { + return super.getRoutingKey(); + } + } + + public boolean isNameRequired() + { + //If the name is null, we require one to be generated by the client so that it will# + //remain valid if we failover (see BLZ-24) + return getQueueName() == null; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java new file mode 100644 index 0000000000..08fd49286b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.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.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.AMQException; + +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueBrowser; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AMQQueueBrowser implements QueueBrowser +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQQueueBrowser.class); + + private AtomicBoolean _isClosed = new AtomicBoolean(); + private final AMQSession _session; + private final AMQQueue _queue; + private final ArrayList _consumers = new ArrayList(); + private final String _messageSelector; + + AMQQueueBrowser(AMQSession session, AMQQueue queue, String messageSelector) throws JMSException + { + _session = session; + _queue = queue; + _messageSelector = ((messageSelector == null) || (messageSelector.trim().length() == 0)) ? null : messageSelector; + // Create Consumer to verify message selector. + BasicMessageConsumer consumer = + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + // Close this consumer as we are not looking to consume only to establish that, at least for now, + // the QB can be created + consumer.close(); + } + + public Queue getQueue() throws JMSException + { + checkState(); + + return _queue; + } + + private void checkState() throws JMSException + { + if (_isClosed.get()) + { + throw new IllegalStateException("Queue Browser"); + } + + if (_session.isClosed()) + { + throw new IllegalStateException("Session is closed"); + } + + } + + public String getMessageSelector() throws JMSException + { + + checkState(); + + return _messageSelector; + } + + public Enumeration getEnumeration() throws JMSException + { + checkState(); + final BasicMessageConsumer consumer = + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + + _consumers.add(consumer); + + return new Enumeration() + { + + Message _nextMessage = consumer == null ? null : consumer.receive(1000); + + public boolean hasMoreElements() + { + _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); + return (_nextMessage != null); + } + + public Object nextElement() + { + Message msg = _nextMessage; + try + { + _logger.info("QB:nextElement about to receive"); + _nextMessage = consumer.receive(1000); + _logger.info("QB:nextElement received:" + _nextMessage); + } + catch (JMSException e) + { + _logger.warn("Exception caught while queue browsing", e); + _nextMessage = null; + } + return msg; + } + }; + } + + public void close() throws JMSException + { + for (BasicMessageConsumer consumer : _consumers) + { + consumer.close(); + } + + _consumers.clear(); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java new file mode 100644 index 0000000000..a8c83d8868 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.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.client; + +import java.io.Serializable; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** + * Need this adaptor class to conform to JMS spec and throw IllegalStateException + * from createDurableSubscriber, unsubscribe, createTopic & createTemporaryTopic + */ +public class AMQQueueSessionAdaptor implements QueueSession, AMQSessionAdapter +{ + //holds a session for delegation + protected final AMQSession _session; + + /** + * Construct an adaptor with a session to wrap + * @param session + */ + public AMQQueueSessionAdaptor(Session session) + { + _session = (AMQSession) session; + } + + public TemporaryQueue createTemporaryQueue() throws JMSException { + return _session.createTemporaryQueue(); + } + + public Queue createQueue(String string) throws JMSException { + return _session.createQueue(string); + } + + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return _session.createReceiver(queue); + } + + public QueueReceiver createReceiver(Queue queue, String string) throws JMSException { + return _session.createReceiver(queue, string); + } + + public QueueSender createSender(Queue queue) throws JMSException { + return _session.createSender(queue); + } + + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return _session.createBrowser(queue); + } + + public QueueBrowser createBrowser(Queue queue, String string) throws JMSException { + return _session.createBrowser(queue, string); + } + + public BytesMessage createBytesMessage() throws JMSException { + return _session.createBytesMessage(); + } + + public MapMessage createMapMessage() throws JMSException { + return _session.createMapMessage(); + } + + public Message createMessage() throws JMSException { + return _session.createMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException { + return _session.createObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException { + return _session.createObjectMessage(serializable); + } + + public StreamMessage createStreamMessage() throws JMSException { + return _session.createStreamMessage(); + } + + public TextMessage createTextMessage() throws JMSException { + return _session.createTextMessage(); + } + + public TextMessage createTextMessage(String string) throws JMSException { + return _session.createTextMessage(string); + } + + public boolean getTransacted() throws JMSException { + return _session.getTransacted(); + } + + public int getAcknowledgeMode() throws JMSException { + return _session.getAcknowledgeMode(); + } + + public void commit() throws JMSException { + _session.commit(); + } + + public void rollback() throws JMSException { + _session.rollback(); + } + + public void close() throws JMSException { + _session.close(); + } + + public void recover() throws JMSException { + _session.recover(); + } + + public MessageListener getMessageListener() throws JMSException { + return _session.getMessageListener(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException { + _session.setMessageListener(messageListener); + } + + public void run() { + _session.run(); + } + + public MessageProducer createProducer(Destination destination) throws JMSException { + return _session.createProducer(destination); + } + + public MessageConsumer createConsumer(Destination destination) throws JMSException { + return _session.createConsumer(destination); + } + + public MessageConsumer createConsumer(Destination destination, String string) throws JMSException { + return _session.createConsumer(destination,string); + } + + public MessageConsumer createConsumer(Destination destination, String string, boolean b) throws JMSException { + return _session.createConsumer(destination,string,b); + } + + //The following methods cannot be called from a QueueSession as per JMS spec + + public Topic createTopic(String string) throws JMSException { + throw new IllegalStateException("Cannot call createTopic from QueueSession"); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String string) throws JMSException { + throw new IllegalStateException("Cannot call createDurableSubscriber from QueueSession"); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String string, String string1, boolean b) throws JMSException { + throw new IllegalStateException("Cannot call createDurableSubscriber from QueueSession"); + } + + public TemporaryTopic createTemporaryTopic() throws JMSException { + throw new IllegalStateException("Cannot call createTemporaryTopic from QueueSession"); + } + + public void unsubscribe(String string) throws JMSException { + throw new IllegalStateException("Cannot call unsubscribe from QueueSession"); + } + + public AMQSession getSession() + { + return _session; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java new file mode 100644 index 0000000000..b5d12d9520 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -0,0 +1,2908 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.io.Serializable; +import java.net.URISyntaxException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +import org.apache.qpid.AMQDisconnectedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidArgumentException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverNoopSupport; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; +import org.apache.qpid.client.message.*; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.util.FlowControllingBlockingQueue; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.jms.Session; +import org.apache.qpid.url.AMQBindingURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ * + * @todo Different FailoverSupport implementation are needed on the same method call, in different situations. For + * example, when failing-over and reestablishing the bindings, the bind cannot be interrupted by a second + * fail-over, if it fails with an exception, the fail-over process should also fail. When binding outside of + * the fail-over process, the retry handler could be used to automatically retry the operation once the connection + * has been reestablished. All fail-over protected operations should be placed in private methods, with + * FailoverSupport passed in by the caller to provide the correct support for the calling context. Sometimes the + * fail-over process sets a nowait flag and uses an async method call instead. + * @todo Two new objects created on every failover supported method call. Consider more efficient ways of doing this, + * after looking at worse bottlenecks first. + */ +public abstract class AMQSession extends Closeable implements Session, QueueSession, TopicSession +{ + + + public static final class IdToConsumerMap + { + private final BasicMessageConsumer[] _fastAccessConsumers = new BasicMessageConsumer[16]; + private final ConcurrentHashMap _slowAccessConsumers = new ConcurrentHashMap(); + + public C get(int id) + { + if ((id & 0xFFFFFFF0) == 0) + { + return (C) _fastAccessConsumers[id]; + } + else + { + return _slowAccessConsumers.get(id); + } + } + + public C put(int id, C consumer) + { + C oldVal; + if ((id & 0xFFFFFFF0) == 0) + { + oldVal = (C) _fastAccessConsumers[id]; + _fastAccessConsumers[id] = consumer; + } + else + { + oldVal = _slowAccessConsumers.put(id, consumer); + } + + return consumer; + + } + + public C remove(int id) + { + C consumer; + if ((id & 0xFFFFFFF0) == 0) + { + consumer = (C) _fastAccessConsumers[id]; + _fastAccessConsumers[id] = null; + } + else + { + consumer = _slowAccessConsumers.remove(id); + } + + return consumer; + + } + + public Collection values() + { + ArrayList values = new ArrayList(); + + for (int i = 0; i < 16; i++) + { + if (_fastAccessConsumers[i] != null) + { + values.add((C) _fastAccessConsumers[i]); + } + } + values.addAll(_slowAccessConsumers.values()); + + return values; + } + + public void clear() + { + _slowAccessConsumers.clear(); + for (int i = 0; i < 16; i++) + { + _fastAccessConsumers[i] = null; + } + } + } + + /** Used for debugging. */ + private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); + + /** + * The default value for immediate flag used by producers created by this session is false. That is, a consumer does + * not need to be attached to a queue. + */ + protected static final boolean DEFAULT_IMMEDIATE = false; + + /** + * The default value for mandatory flag used by producers created by this session is true. That is, server will not + * silently drop messages where no queue is connected to the exchange for the message. + */ + protected static final boolean DEFAULT_MANDATORY = true; + + /** System property to enable strict AMQP compliance. */ + public static final String STRICT_AMQP = "STRICT_AMQP"; + + /** Strict AMQP default setting. */ + public static final String STRICT_AMQP_DEFAULT = "false"; + + /** System property to enable failure if strict AMQP compliance is violated. */ + public static final String STRICT_AMQP_FATAL = "STRICT_AMQP_FATAL"; + + /** Strickt AMQP failure default. */ + public static final String STRICT_AMQP_FATAL_DEFAULT = "true"; + + /** System property to enable immediate message prefetching. */ + public static final String IMMEDIATE_PREFETCH = "IMMEDIATE_PREFETCH"; + + /** Immediate message prefetch default. */ + public static final String IMMEDIATE_PREFETCH_DEFAULT = "false"; + + /** The connection to which this session belongs. */ + protected AMQConnection _connection; + + /** Used to indicate whether or not this is a transactional session. */ + protected boolean _transacted; + + /** Holds the sessions acknowledgement mode. */ + protected final int _acknowledgeMode; + + /** Holds this session unique identifier, used to distinguish it from other sessions. */ + protected int _channelId; + + private int _ticket; + + /** Holds the high mark for prefetched message, at which the session is suspended. */ + private int _defaultPrefetchHighMark; + + /** Holds the low mark for prefetched messages, below which the session is resumed. */ + private int _defaultPrefetchLowMark; + + /** Holds the message listener, if any, which is attached to this session. */ + private MessageListener _messageListener = null; + + /** Used to indicate that this session has been started at least once. */ + private AtomicBoolean _startedAtLeastOnce = new AtomicBoolean(false); + + /** + * Used to reference durable subscribers so that requests for unsubscribe can be handled correctly. Note this only + * keeps a record of subscriptions which have been created in the current instance. It does not remember + * subscriptions between executions of the client. + */ + protected final ConcurrentHashMap _subscriptions = + new ConcurrentHashMap(); + + /** + * Holds a mapping from message consumers to their identifying names, so that their subscriptions may be looked + * up in the {@link #_subscriptions} map. + */ + protected final ConcurrentHashMap _reverseSubscriptionMap = + new ConcurrentHashMap(); + + /** + * Used to hold incoming messages. + * + * @todo Weaken the type once {@link FlowControllingBlockingQueue} implements Queue. + */ + protected final FlowControllingBlockingQueue _queue; + + /** Holds the highest received delivery tag. */ + private final AtomicLong _highestDeliveryTag = new AtomicLong(-1); + private final AtomicLong _rollbackMark = new AtomicLong(-1); + + /** All the not yet acknowledged message tags */ + protected ConcurrentLinkedQueue _unacknowledgedMessageTags = new ConcurrentLinkedQueue(); + + /** All the delivered message tags */ + protected ConcurrentLinkedQueue _deliveredMessageTags = new ConcurrentLinkedQueue(); + + /** Holds the dispatcher thread for this session. */ + protected Dispatcher _dispatcher; + + /** Holds the message factory factory for this session. */ + protected MessageFactoryRegistry _messageFactoryRegistry; + + /** Holds all of the producers created by this session, keyed by their unique identifiers. */ + private Map _producers = new ConcurrentHashMap(); + + /** + * Used as a source of unique identifiers so that the consumers can be tagged to match them to BasicConsume + * methods. + */ + private int _nextTag = 1; + + /** + * Maps from identifying tags to message consumers, in order to pass dispatch incoming messages to the right + * consumer. + */ + protected final IdToConsumerMap _consumers = new IdToConsumerMap(); + + //Map _consumers = + //new ConcurrentHashMap(); + + /** + * Contains a list of consumers which have been removed but which might still have + * messages to acknowledge, eg in client ack or transacted modes + */ + private CopyOnWriteArrayList _removedConsumers = new CopyOnWriteArrayList(); + + /** Provides a count of consumers on destinations, in order to be able to know if a destination has consumers. */ + private ConcurrentHashMap _destinationConsumerCount = + new ConcurrentHashMap(); + + /** + * Used as a source of unique identifiers for producers within the session. + * + *

Access to this id does not require to be synchronized since according to the JMS specification only one + * thread of control is allowed to create producers for any given session instance. + */ + private long _nextProducerId; + + /** + * Set when recover is called. This is to handle the case where recover() is called by application code during + * onMessage() processing to enure that an auto ack is not sent. + */ + private boolean _inRecovery; + + /** Used to indicates that the connection to which this session belongs, has been stopped. */ + private boolean _connectionStopped; + + /** Used to indicate that this session has a message listener attached to it. */ + private boolean _hasMessageListeners; + + /** Used to indicate that this session has been suspended. */ + private boolean _suspended; + + /** + * Used to protect the suspension of this session, so that critical code can be executed during suspension, + * without the session being resumed by other threads. + */ + private final Object _suspensionLock = new Object(); + + /** + * Used to ensure that onlt the first call to start the dispatcher can unsuspend the channel. + * + * @todo This is accessed only within a synchronized method, so does not need to be atomic. + */ + protected final AtomicBoolean _firstDispatcher = new AtomicBoolean(true); + + /** Used to indicate that the session should start pre-fetching messages as soon as it is started. */ + protected final boolean _immediatePrefetch; + + /** Indicates that warnings should be generated on violations of the strict AMQP. */ + protected final boolean _strictAMQP; + + /** Indicates that runtime exceptions should be generated on vilations of the strict AMQP. */ + protected final boolean _strictAMQPFATAL; + private final Object _messageDeliveryLock = new Object(); + + /** Session state : used to detect if commit is a) required b) allowed , i.e. does the tx span failover. */ + private boolean _dirty; + /** Has failover occured on this session with outstanding actions to commit? */ + private boolean _failedOverDirty; + + private static final class FlowControlIndicator + { + private volatile boolean _flowControl = true; + + public synchronized void setFlowControl(boolean flowControl) + { + _flowControl = flowControl; + notify(); + } + + public boolean getFlowControl() + { + return _flowControl; + } + } + + /** Flow control */ + private FlowControlIndicator _flowControl = new FlowControlIndicator(); + + /** + * Creates a new session on a connection. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param messageFactoryRegistry The message factory factory for the session. + * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. + */ + protected AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, + MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetchHighMark, int defaultPrefetchLowMark) + { + + _strictAMQP = Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP, STRICT_AMQP_DEFAULT)); + _strictAMQPFATAL = + Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP_FATAL, STRICT_AMQP_FATAL_DEFAULT)); + _immediatePrefetch = + _strictAMQP + || Boolean.parseBoolean(System.getProperties().getProperty(IMMEDIATE_PREFETCH, IMMEDIATE_PREFETCH_DEFAULT)); + + _connection = con; + _transacted = transacted; + if (transacted) + { + _acknowledgeMode = javax.jms.Session.SESSION_TRANSACTED; + } + else + { + _acknowledgeMode = acknowledgeMode; + } + + _channelId = channelId; + _messageFactoryRegistry = messageFactoryRegistry; + _defaultPrefetchHighMark = defaultPrefetchHighMark; + _defaultPrefetchLowMark = defaultPrefetchLowMark; + + if (_acknowledgeMode == NO_ACKNOWLEDGE) + { + _queue = + new FlowControllingBlockingQueue(_defaultPrefetchHighMark, _defaultPrefetchLowMark, + new FlowControllingBlockingQueue.ThresholdListener() + { + private final AtomicBoolean _suspendState = new AtomicBoolean(); + + public void aboveThreshold(int currentValue) + { + _logger.debug( + "Above threshold(" + _defaultPrefetchHighMark + + ") so suspending channel. Current value is " + currentValue); + _suspendState.set(true); + new Thread(new SuspenderRunner(_suspendState)).start(); + + } + + public void underThreshold(int currentValue) + { + _logger.debug( + "Below threshold(" + _defaultPrefetchLowMark + + ") so unsuspending channel. Current value is " + currentValue); + _suspendState.set(false); + new Thread(new SuspenderRunner(_suspendState)).start(); + + } + }); + } + else + { + _queue = new FlowControllingBlockingQueue(_defaultPrefetchHighMark, null); + } + } + + /** + * Creates a new session on a connection with the default message factory factory. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. + */ + AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, + int defaultPrefetchLow) + { + this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, + defaultPrefetchLow); + } + + // ===== JMS Session methods. + + /** + * Closes the session with no timeout. + * + * @throws JMSException If the JMS provider fails to close the session due to some internal error. + */ + public void close() throws JMSException + { + close(-1); + } + + public void checkNotClosed() throws JMSException + { + try + { + super.checkNotClosed(); + } + catch (IllegalStateException ise) + { + // if the Connection has closed then we should throw any exception that has occured that we were not waiting for + AMQStateManager manager = _connection.getProtocolHandler().getStateManager(); + + if (manager.getCurrentState().equals(AMQState.CONNECTION_CLOSED) && manager.getLastException() != null) + { + ise.setLinkedException(manager.getLastException()); + } + + throw ise; + } + } + + public BytesMessage createBytesMessage() throws JMSException + { + checkNotClosed(); + return new JMSBytesMessage(getMessageDelegateFactory()); + } + + /** + * Acknowledges all unacknowledged messages on the session, for all message consumers on the session. + * + * @throws IllegalStateException If the session is closed. + */ + public void acknowledge() throws IllegalStateException + { + if (isClosed()) + { + throw new IllegalStateException("Session is already closed"); + } + else if (hasFailedOver()) + { + throw new IllegalStateException("has failed over"); + } + + while (true) + { + Long tag = _unacknowledgedMessageTags.poll(); + if (tag == null) + { + break; + } + acknowledgeMessage(tag, false); + } + } + + /** + * Acknowledge one or many messages. + * + * @param deliveryTag The tag of the last message to be acknowledged. + * @param multiple true to acknowledge all messages up to and including the one specified by the + * delivery tag, false to just acknowledge that message. + * + * @todo Be aware of possible changes to parameter order as versions change. + */ + public abstract void acknowledgeMessage(long deliveryTag, boolean multiple); + + public MethodRegistry getMethodRegistry() + { + MethodRegistry methodRegistry = getProtocolHandler().getMethodRegistry(); + return methodRegistry; + } + + /** + * Binds the named queue, with the specified routing key, to the named exchange. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param queueName The name of the queue to bind. + * @param routingKey The routing key to bind the queue with. + * @param arguments Additional arguments. + * @param exchangeName The exchange to bind the queue on. + * + * @throws AMQException If the queue cannot be bound for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + * @todo Document the additional arguments that may be passed in the field table. Are these for headers exchanges? + */ + public void bindQueue(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, + final AMQShortString exchangeName, final AMQDestination destination) throws AMQException + { + /*new FailoverRetrySupport(new FailoverProtectedOperation()*/ + new FailoverNoopSupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + sendQueueBind(queueName, routingKey, arguments, exchangeName, destination); + return null; + } + }, _connection).execute(); + } + + public void addBindingKey(C consumer, AMQDestination amqd, String routingKey) throws AMQException + { + if (consumer.getQueuename() != null) + { + bindQueue(consumer.getQueuename(), new AMQShortString(routingKey), new FieldTable(), amqd.getExchangeName(), amqd); + } + } + + public abstract void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, + final AMQShortString exchangeName, AMQDestination destination) throws AMQException, FailoverException; + + /** + * Closes the session. + * + *

Note that this operation succeeds automatically if a fail-over interupts the sycnronous request to close + * the channel. This is because the channel is marked as closed before the request to close it is made, so the + * fail-over should not re-open it. + * + * @param timeout The timeout in milliseconds to wait for the session close acknoledgement from the broker. + * + * @throws JMSException If the JMS provider fails to close the session due to some internal error. + * @todo Be aware of possible changes to parameter order as versions change. + * @todo Not certain about the logic of ignoring the failover exception, because the channel won't be + * re-opened. May need to examine this more carefully. + * @todo Note that taking the failover mutex doesn't prevent this operation being interrupted by a failover, + * because the failover process sends the failover event before acquiring the mutex itself. + */ + public void close(long timeout) throws JMSException + { + if (_logger.isInfoEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + _logger.info("Closing session: " + this); // + ":" + // + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + } + + // Ensure we only try and close an open session. + if (!_closed.getAndSet(true)) + { + synchronized (getFailoverMutex()) + { + // We must close down all producers and consumers in an orderly fashion. This is the only method + // that can be called from a different thread of control from the one controlling the session. + synchronized (_messageDeliveryLock) + { + // we pass null since this is not an error case + closeProducersAndConsumers(null); + + try + { + sendClose(timeout); + } + catch (AMQException e) + { + JMSException jmse = new JMSException("Error closing session: " + e); + jmse.setLinkedException(e); + throw jmse; + } + // This is ignored because the channel is already marked as closed so the fail-over process will + // not re-open it. + catch (FailoverException e) + { + _logger.debug( + "Got FailoverException during channel close, ignored as channel already marked as closed."); + } + finally + { + _connection.deregisterSession(_channelId); + } + } + } + } + } + + public abstract void sendClose(long timeout) throws AMQException, FailoverException; + + /** + * Called when the server initiates the closure of the session unilaterally. + * + * @param e the exception that caused this session to be closed. Null causes the + */ + public void closed(Throwable e) throws JMSException + { + // This method needs to be improved. Throwables only arrive here from the mina : exceptionRecived + // calls through connection.closeAllSessions which is also called by the public connection.close() + // with a null cause + // When we are closing the Session due to a protocol session error we simply create a new AMQException + // with the correct error code and text this is cleary WRONG as the instanceof check below will fail. + // We need to determin here if the connection should be + + if (e instanceof AMQDisconnectedException) + { + if (_dispatcher != null) + { + // Failover failed and ain't coming back. Knife the dispatcher. + _dispatcher.interrupt(); + } + } + + if (!_closed.getAndSet(true)) + { + synchronized (getFailoverMutex()) + { + synchronized (_messageDeliveryLock) + { + // An AMQException has an error code and message already and will be passed in when closure occurs as a + // result of a channel close request + AMQException amqe; + if (e instanceof AMQException) + { + amqe = (AMQException) e; + } + else + { + amqe = new AMQException("Closing session forcibly", e); + } + + _connection.deregisterSession(_channelId); + closeProducersAndConsumers(amqe); + } + } + } + } + + /** + * Commits all messages done in this transaction and releases any locks currently held. + * + *

If the commit fails, because the commit itself is interrupted by a fail-over between requesting that the + * commit be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown. + * The client will be unable to determine whether or not the commit actually happened on the broker in this case. + * + * @throws JMSException If the JMS provider fails to commit the transaction due to some internal error. This does + * not mean that the commit is known to have failed, merely that it is not known whether it + * failed or not. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void commit() throws JMSException + { + checkTransacted(); + + try + { + + // TGM FIXME: what about failover? + // Acknowledge all delivered messages + while (true) + { + Long tag = _deliveredMessageTags.poll(); + if (tag == null) + { + break; + } + + acknowledgeMessage(tag, false); + } + // Commits outstanding messages and acknowledgments + sendCommit(); + markClean(); + } + catch (AMQException e) + { + throw new JMSAMQException("Failed to commit: " + e.getMessage(), e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e); + } + } + + public abstract void sendCommit() throws AMQException, FailoverException; + + + public void confirmConsumerCancelled(int consumerTag) + { + + // Remove the consumer from the map + C consumer = _consumers.get(consumerTag); + if (consumer != null) + { + if (!consumer.isNoConsume()) // Normal Consumer + { + // Clean the Maps up first + // Flush any pending messages for this consumerTag + if (_dispatcher != null) + { + _logger.info("Dispatcher is not null"); + } + else + { + _logger.info("Dispatcher is null so created stopped dispatcher"); + startDispatcherIfNecessary(true); + } + + _dispatcher.rejectPending(consumer); + } + else // Queue Browser + { + // Just close the consumer + // fixme the CancelOK is being processed before the arriving messages.. + // The dispatcher is still to process them so the server sent in order but the client + // has yet to receive before the close comes in. + + // consumer.markClosed(); + + if (consumer.isAutoClose()) + { + // There is a small window where the message is between the two queues in the dispatcher. + if (consumer.isClosed()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing consumer:" + consumer.debugIdentity()); + } + + deregisterConsumer(consumer); + } + else + { + _queue.add(new CloseConsumerMessage(consumer)); + } + } + } + } + } + + public QueueBrowser createBrowser(Queue queue) throws JMSException + { + if (isStrictAMQP()) + { + throw new UnsupportedOperationException(); + } + + return createBrowser(queue, null); + } + + public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException + { + if (isStrictAMQP()) + { + throw new UnsupportedOperationException(); + } + + checkNotClosed(); + checkValidQueue(queue); + + return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector); + } + + public MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal) + throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + messageSelector, null, true, true); + } + + public MessageConsumer createConsumer(Destination destination) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), null, null, + false, false); + } + + public C createExclusiveConsumer(Destination destination) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, true, null, null, + false, false); + } + + public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, (destination instanceof Topic), + messageSelector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) + throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, (destination instanceof Topic), + messageSelector, null, false, false); + } + + public MessageConsumer createExclusiveConsumer(Destination destination, String messageSelector, boolean noLocal) + throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, true, + messageSelector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, + String selector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, + boolean exclusive, String selector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, + String selector, FieldTable rawSelector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, prefetch, prefetch / 2, noLocal, exclusive, selector, rawSelector, false, false); + } + + public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, + boolean exclusive, String selector, FieldTable rawSelector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, false, + false); + } + + public abstract TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException; + + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) + throws JMSException + { + checkNotClosed(); + checkValidTopic(topic); + if (_subscriptions.containsKey(name)) + { + _subscriptions.get(name).close(); + } + AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection); + C consumer = (C) createConsumer(dest, messageSelector, noLocal); + TopicSubscriberAdaptor subscriber = new TopicSubscriberAdaptor(dest, consumer); + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); + + return subscriber; + } + + public MapMessage createMapMessage() throws JMSException + { + checkNotClosed(); + return new JMSMapMessage(getMessageDelegateFactory()); + } + + public javax.jms.Message createMessage() throws JMSException + { + return createBytesMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException + { + checkNotClosed(); + return (ObjectMessage) new JMSObjectMessage(getMessageDelegateFactory()); + } + + public ObjectMessage createObjectMessage(Serializable object) throws JMSException + { + ObjectMessage msg = createObjectMessage(); + msg.setObject(object); + + return msg; + } + + public P createProducer(Destination destination) throws JMSException + { + return createProducerImpl(destination, DEFAULT_MANDATORY, DEFAULT_IMMEDIATE); + } + + public P createProducer(Destination destination, boolean immediate) throws JMSException + { + return createProducerImpl(destination, DEFAULT_MANDATORY, immediate); + } + + public P createProducer(Destination destination, boolean mandatory, boolean immediate) + throws JMSException + { + return createProducerImpl(destination, mandatory, immediate); + } + + public P createProducer(Destination destination, boolean mandatory, boolean immediate, + boolean waitUntilSent) throws JMSException + { + return createProducerImpl(destination, mandatory, immediate, waitUntilSent); + } + + public TopicPublisher createPublisher(Topic topic) throws JMSException + { + checkNotClosed(); + + return new TopicPublisherAdapter((P) createProducer(topic, false, false), topic); + } + + public Queue createQueue(String queueName) throws JMSException + { + checkNotClosed(); + if (queueName.indexOf('/') == -1) + { + return new AMQQueue(getDefaultQueueExchangeName(), new AMQShortString(queueName)); + } + else + { + try + { + return new AMQQueue(new AMQBindingURL(queueName)); + } + catch (URISyntaxException urlse) + { + JMSException jmse = new JMSException(urlse.getReason()); + jmse.setLinkedException(urlse); + + throw jmse; + } + } + } + + /** + * Declares the named queue. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param name The name of the queue to declare. + * @param autoDelete + * @param durable Flag to indicate that the queue is durable. + * @param exclusive Flag to indicate that the queue is exclusive to this client. + * + * @throws AMQException If the queue cannot be declared for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive) throws AMQException + { + createQueue(name, autoDelete, durable, exclusive, null); + } + + /** + * Declares the named queue. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param name The name of the queue to declare. + * @param autoDelete + * @param durable Flag to indicate that the queue is durable. + * @param exclusive Flag to indicate that the queue is exclusive to this client. + * @param arguments Arguments used to set special properties of the queue + * + * @throws AMQException If the queue cannot be declared for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive, final Map arguments) throws AMQException + { + new FailoverRetrySupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + sendCreateQueue(name, autoDelete, durable, exclusive, arguments); + return null; + } + }, _connection).execute(); + } + + public abstract void sendCreateQueue(AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive, final Map arguments) throws AMQException, FailoverException; + + /** + * Creates a QueueReceiver + * + * @param destination + * + * @return QueueReceiver - a wrapper around our MessageConsumer + * + * @throws JMSException + */ + public QueueReceiver createQueueReceiver(Destination destination) throws JMSException + { + checkValidDestination(destination); + AMQQueue dest = (AMQQueue) destination; + C consumer = (C) createConsumer(destination); + + return new QueueReceiverAdaptor(dest, consumer); + } + + /** + * Creates a QueueReceiver using a message selector + * + * @param destination + * @param messageSelector + * + * @return QueueReceiver - a wrapper around our MessageConsumer + * + * @throws JMSException + */ + public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException + { + checkValidDestination(destination); + AMQQueue dest = (AMQQueue) destination; + C consumer = (C) createConsumer(destination, messageSelector); + + return new QueueReceiverAdaptor(dest, consumer); + } + + /** + * Creates a QueueReceiver wrapping a MessageConsumer + * + * @param queue + * + * @return QueueReceiver + * + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue) throws JMSException + { + checkNotClosed(); + AMQQueue dest = (AMQQueue) queue; + C consumer = (C) createConsumer(dest); + + return new QueueReceiverAdaptor(dest, consumer); + } + + /** + * Creates a QueueReceiver wrapping a MessageConsumer using a message selector + * + * @param queue + * @param messageSelector + * + * @return QueueReceiver + * + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException + { + checkNotClosed(); + AMQQueue dest = (AMQQueue) queue; + C consumer = (C) createConsumer(dest, messageSelector); + + return new QueueReceiverAdaptor(dest, consumer); + } + + public QueueSender createSender(Queue queue) throws JMSException + { + checkNotClosed(); + + // return (QueueSender) createProducer(queue); + return new QueueSenderAdapter(createProducer(queue), queue); + } + + public StreamMessage createStreamMessage() throws JMSException + { + // This method needs to be improved. Throwables only arrive here from the mina : exceptionRecived + // calls through connection.closeAllSessions which is also called by the public connection.close() + // with a null cause + // When we are closing the Session due to a protocol session error we simply create a new AMQException + // with the correct error code and text this is cleary WRONG as the instanceof check below will fail. + // We need to determin here if the connection should be + + synchronized (getFailoverMutex()) + { + checkNotClosed(); + + return new JMSStreamMessage(getMessageDelegateFactory()); + } + } + + /** + * Creates a non-durable subscriber + * + * @param topic + * + * @return TopicSubscriber - a wrapper round our MessageConsumer + * + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic) throws JMSException + { + checkNotClosed(); + AMQTopic dest = checkValidTopic(topic); + + // AMQTopic dest = new AMQTopic(topic.getTopicName()); + return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest)); + } + + /** + * Creates a non-durable subscriber with a message selector + * + * @param topic + * @param messageSelector + * @param noLocal + * + * @return TopicSubscriber - a wrapper round our MessageConsumer + * + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException + { + checkNotClosed(); + AMQTopic dest = checkValidTopic(topic); + + // AMQTopic dest = new AMQTopic(topic.getTopicName()); + return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest, messageSelector, noLocal)); + } + + public TemporaryQueue createTemporaryQueue() throws JMSException + { + checkNotClosed(); + try + { + AMQTemporaryQueue result = new AMQTemporaryQueue(this); + + // this is done so that we can produce to a temporary queue before we create a consumer + result.setQueueName(result.getRoutingKey()); + createQueue(result.getAMQQueueName(), result.isAutoDelete(), + result.isDurable(), result.isExclusive()); + bindQueue(result.getAMQQueueName(), result.getRoutingKey(), + new FieldTable(), result.getExchangeName(), result); + return result; + } + catch (Exception e) + { + JMSException ex = new JMSException("Cannot create temporary queue"); + ex.setLinkedException(e); + e.printStackTrace(); + throw ex; + } + } + + public TemporaryTopic createTemporaryTopic() throws JMSException + { + checkNotClosed(); + + return new AMQTemporaryTopic(this); + } + + public TextMessage createTextMessage() throws JMSException + { + synchronized (getFailoverMutex()) + { + checkNotClosed(); + + return new JMSTextMessage(getMessageDelegateFactory()); + } + } + + protected Object getFailoverMutex() + { + return _connection.getFailoverMutex(); + } + + public TextMessage createTextMessage(String text) throws JMSException + { + + TextMessage msg = createTextMessage(); + msg.setText(text); + + return msg; + } + + public Topic createTopic(String topicName) throws JMSException + { + checkNotClosed(); + + if (topicName.indexOf('/') == -1) + { + return new AMQTopic(getDefaultTopicExchangeName(), new AMQShortString(topicName)); + } + else + { + try + { + return new AMQTopic(new AMQBindingURL(topicName)); + } + catch (URISyntaxException urlse) + { + JMSException jmse = new JMSException(urlse.getReason()); + jmse.setLinkedException(urlse); + + throw jmse; + } + } + } + + public void declareExchange(AMQShortString name, AMQShortString type, boolean nowait) throws AMQException + { + declareExchange(name, type, getProtocolHandler(), nowait); + } + + public int getAcknowledgeMode() throws JMSException + { + checkNotClosed(); + + return _acknowledgeMode; + } + + public AMQConnection getAMQConnection() + { + return _connection; + } + + public int getChannelId() + { + return _channelId; + } + + public int getDefaultPrefetch() + { + return _defaultPrefetchHighMark; + } + + public int getDefaultPrefetchHigh() + { + return _defaultPrefetchHighMark; + } + + public int getDefaultPrefetchLow() + { + return _defaultPrefetchLowMark; + } + + public AMQShortString getDefaultQueueExchangeName() + { + return _connection.getDefaultQueueExchangeName(); + } + + public AMQShortString getDefaultTopicExchangeName() + { + return _connection.getDefaultTopicExchangeName(); + } + + public MessageListener getMessageListener() throws JMSException + { + // checkNotClosed(); + return _messageListener; + } + + public AMQShortString getTemporaryQueueExchangeName() + { + return _connection.getTemporaryQueueExchangeName(); + } + + public AMQShortString getTemporaryTopicExchangeName() + { + return _connection.getTemporaryTopicExchangeName(); + } + + public int getTicket() + { + return _ticket; + } + + public boolean getTransacted() throws JMSException + { + checkNotClosed(); + + return _transacted; + } + + public boolean hasConsumer(Destination destination) + { + AtomicInteger counter = _destinationConsumerCount.get(destination); + + return (counter != null) && (counter.get() != 0); + } + + public boolean isStrictAMQP() + { + return _strictAMQP; + } + + public boolean isSuspended() + { + return _suspended; + } + + protected void addUnacknowledgedMessage(long id) + { + _unacknowledgedMessageTags.add(id); + } + + protected void addDeliveredMessage(long id) + { + _deliveredMessageTags.add(id); + } + + /** + * Invoked by the MINA IO thread (indirectly) when a message is received from the transport. Puts the message onto + * the queue read by the dispatcher. + * + * @param message the message that has been received + */ + public void messageReceived(UnprocessedMessage message) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Message[" + message.toString() + "] received in session"); + } + _highestDeliveryTag.set(message.getDeliveryTag()); + _queue.add(message); + } + + public void declareAndBind(AMQDestination amqd) + throws + AMQException + { + AMQProtocolHandler protocolHandler = getProtocolHandler(); + declareExchange(amqd, protocolHandler, false); + AMQShortString queueName = declareQueue(amqd, protocolHandler, false); + bindQueue(queueName, amqd.getRoutingKey(), new FieldTable(), amqd.getExchangeName(), amqd); + } + + /** + * Stops message delivery in this session, and restarts message delivery with the oldest unacknowledged message. + * + *

All consumers deliver messages in a serial order. Acknowledging a received message automatically acknowledges + * all messages that have been delivered to the client. + * + *

Restarting a session causes it to take the following actions: + * + *

    + *
  • Stop message delivery.
  • + *
  • Mark all messages that might have been delivered but not acknowledged as "redelivered". + *
  • Restart the delivery sequence including all unacknowledged messages that had been previously delivered. + * Redelivered messages do not have to be delivered in exactly their original delivery order.
  • + *
+ * + *

If the recover operation is interrupted by a fail-over, between asking that the broker begin recovery and + * receiving acknolwedgement that it hasm then a JMSException will be thrown. In this case it will not be possible + * for the client to determine whether the broker is going to recover the session or not. + * + * @throws JMSException If the JMS provider fails to stop and restart message delivery due to some internal error. + * Not that this does not necessarily mean that the recovery has failed, but simply that it is + * not possible to tell if it has or not. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void recover() throws JMSException + { + // Ensure that the session is open. + checkNotClosed(); + + // Ensure that the session is not transacted. + checkNotTransacted(); + + // this is set only here, and the before the consumer's onMessage is called it is set to false + _inRecovery = true; + try + { + + boolean isSuspended = isSuspended(); + + if (!isSuspended) + { + suspendChannel(true); + } + + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + + sendRecover(); + + if (!isSuspended) + { + suspendChannel(false); + } + } + catch (AMQException e) + { + throw new JMSAMQException("Recover failed: " + e.getMessage(), e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Recovery was interrupted by fail-over. Recovery status is not known.", e); + } + } + + protected abstract void sendRecover() throws AMQException, FailoverException; + + public void rejectMessage(UnprocessedMessage message, boolean requeue) + { + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting Unacked message:" + message.getDeliveryTag()); + } + + rejectMessage(message.getDeliveryTag(), requeue); + } + + public void rejectMessage(AbstractJMSMessage message, boolean requeue) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting Abstract message:" + message.getDeliveryTag()); + } + + rejectMessage(message.getDeliveryTag(), requeue); + + } + + public abstract void rejectMessage(long deliveryTag, boolean requeue); + + /** + * Commits all messages done in this transaction and releases any locks currently held. + * + *

If the rollback fails, because the rollback itself is interrupted by a fail-over between requesting that the + * rollback be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown. + * The client will be unable to determine whether or not the rollback actually happened on the broker in this case. + * + * @throws JMSException If the JMS provider fails to rollback the transaction due to some internal error. This does + * not mean that the rollback is known to have failed, merely that it is not known whether it + * failed or not. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void rollback() throws JMSException + { + synchronized (_suspensionLock) + { + checkTransacted(); + + try + { + boolean isSuspended = isSuspended(); + + if (!isSuspended) + { + suspendChannel(true); + } + + releaseForRollback(); + + sendRollback(); + + markClean(); + + if (!isSuspended) + { + suspendChannel(false); + } + } + catch (AMQException e) + { + throw new JMSAMQException("Failed to rollback: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Fail-over interrupted rollback. Status of the rollback is uncertain.", e); + } + } + } + + public abstract void releaseForRollback(); + + public abstract void sendRollback() throws AMQException, FailoverException; + + public void run() + { + throw new java.lang.UnsupportedOperationException(); + } + + public void setMessageListener(MessageListener listener) throws JMSException + { + // checkNotClosed(); + // + // if (_dispatcher != null && !_dispatcher.connectionStopped()) + // { + // throw new javax.njms.IllegalStateException("Attempt to set listener while session is started."); + // } + // + // // We are stopped + // for (Iterator i = _consumers.values().iterator(); i.hasNext();) + // { + // BasicMessageConsumer consumer = i.next(); + // + // if (consumer.isReceiving()) + // { + // throw new javax.njms.IllegalStateException("Another thread is already receiving synchronously."); + // } + // } + // + // _messageListener = listener; + // + // for (Iterator i = _consumers.values().iterator(); i.hasNext();) + // { + // i.next().setMessageListener(_messageListener); + // } + + } + + /*public void setTicket(int ticket) + { + _ticket = ticket; + }*/ + + public void unsubscribe(String name) throws JMSException + { + checkNotClosed(); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) + { + // send a queue.delete for the subscription + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + _subscriptions.remove(name); + _reverseSubscriptionMap.remove(subscriber); + } + else + { + if (_strictAMQP) + { + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); + } + else + { + _logger.warn("Unable to determine if subscription already exists for '" + name + "' for unsubscribe." + + " Requesting queue deletion regardless."); + } + + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + } + else // Queue Browser + { + + if (isQueueBound(getDefaultTopicExchangeName(), AMQTopic.getDurableTopicQueueName(name, _connection))) + { + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + } + else + { + throw new InvalidDestinationException("Unknown subscription exchange:" + name); + } + } + } + } + + protected C createConsumerImpl(final Destination destination, final int prefetchHigh, + final int prefetchLow, final boolean noLocal, final boolean exclusive, String selector, final FieldTable rawSelector, + final boolean noConsume, final boolean autoClose) throws JMSException + { + checkTemporaryDestination(destination); + + final String messageSelector; + + if (_strictAMQP && !((selector == null) || selector.equals(""))) + { + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("Selectors not currently supported by AMQP."); + } + else + { + messageSelector = null; + } + } + else + { + messageSelector = selector; + } + + return new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public C execute() throws JMSException, FailoverException + { + checkNotClosed(); + + AMQDestination amqd = (AMQDestination) destination; + + final AMQProtocolHandler protocolHandler = getProtocolHandler(); + // TODO: Define selectors in AMQP + // TODO: construct the rawSelector from the selector string if rawSelector == null + final FieldTable ft = FieldTableFactory.newFieldTable(); + // if (rawSelector != null) + // ft.put("headers", rawSelector.getDataAsBytes()); + // rawSelector is used by HeadersExchange and is not a JMS Selector + if (rawSelector != null) + { + ft.addAll(rawSelector); + } + + if (messageSelector != null) + { + ft.put(new AMQShortString("x-filter-jms-selector"), messageSelector); + } + + C consumer = createMessageConsumer(amqd, prefetchHigh, prefetchLow, + noLocal, exclusive, messageSelector, ft, noConsume, autoClose); + + if (_messageListener != null) + { + consumer.setMessageListener(_messageListener); + } + + try + { + registerConsumer(consumer, false); + } + catch (AMQInvalidArgumentException ise) + { + JMSException ex = new InvalidSelectorException(ise.getMessage()); + ex.setLinkedException(ise); + throw ex; + } + catch (AMQInvalidRoutingKeyException e) + { + JMSException ide = + new InvalidDestinationException("Invalid routing key:" + amqd.getRoutingKey().toString()); + ide.setLinkedException(e); + throw ide; + } + catch (AMQException e) + { + JMSException ex = new JMSException("Error registering consumer: " + e); + + ex.setLinkedException(e); + throw ex; + } + + synchronized (destination) + { + _destinationConsumerCount.putIfAbsent(destination, new AtomicInteger()); + _destinationConsumerCount.get(destination).incrementAndGet(); + } + + return consumer; + } + }, _connection).execute(); + } + + public abstract C createMessageConsumer(final AMQDestination destination, final int prefetchHigh, + final int prefetchLow, final boolean noLocal, final boolean exclusive, String selector, final FieldTable arguments, + final boolean noConsume, final boolean autoClose) throws JMSException; + + /** + * Called by the MessageConsumer when closing, to deregister the consumer from the map from consumerTag to consumer + * instance. + * + * @param consumer the consum + */ + void deregisterConsumer(C consumer) + { + if (_consumers.remove(consumer.getConsumerTag()) != null) + { + String subscriptionName = _reverseSubscriptionMap.remove(consumer); + if (subscriptionName != null) + { + _subscriptions.remove(subscriptionName); + } + + Destination dest = consumer.getDestination(); + synchronized (dest) + { + if (_destinationConsumerCount.get(dest).decrementAndGet() == 0) + { + _destinationConsumerCount.remove(dest); + } + } + + // Consumers that are closed in a transaction must be stored + // so that messages they have received can be acknowledged on commit + if (_transacted) + { + _removedConsumers.add(consumer); + } + } + } + + void deregisterProducer(long producerId) + { + _producers.remove(new Long(producerId)); + } + + boolean isInRecovery() + { + return _inRecovery; + } + + boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException + { + return isQueueBound(exchangeName, queueName, null); + } + + /** + * Tests whether or not the specified queue is bound to the specified exchange under a particular routing key. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param exchangeName The exchange name to test for binding against. + * @param queueName The queue name to check if bound. + * @param routingKey The routing key to check if the queue is bound under. + * + * @return true if the queue is bound to the exchange and routing key, false if not. + * + * @throws JMSException If the query fails for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public abstract boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey) + throws JMSException; + + public abstract boolean isQueueBound(final AMQDestination destination) throws JMSException; + + /** + * Called to mark the session as being closed. Useful when the session needs to be made invalid, e.g. after failover + * when the client has veoted resubscription.

The caller of this method must already hold the failover mutex. + */ + void markClosed() + { + _closed.set(true); + _connection.deregisterSession(_channelId); + markClosedProducersAndConsumers(); + + } + + /** + * Resubscribes all producers and consumers. This is called when performing failover. + * + * @throws AMQException + */ + void resubscribe() throws AMQException + { + if (_dirty) + { + _failedOverDirty = true; + } + + _rollbackMark.set(-1); + resubscribeProducers(); + resubscribeConsumers(); + } + + void setHasMessageListeners() + { + _hasMessageListeners = true; + } + + void setInRecovery(boolean inRecovery) + { + _inRecovery = inRecovery; + } + + /** + * Starts the session, which ensures that it is not suspended and that its event dispatcher is running. + * + * @throws AMQException If the session cannot be started for any reason. + * @todo This should be controlled by _stopped as it pairs with the stop method fixme or check the + * FlowControlledBlockingQueue _queue to see if we have flow controlled. will result in sending Flow messages + * for each subsequent call to flow.. only need to do this if we have called stop. + */ + void start() throws AMQException + { + // Check if the session has perviously been started and suspended, in which case it must be unsuspended. + if (_startedAtLeastOnce.getAndSet(true)) + { + suspendChannel(false); + } + + // If the event dispatcher is not running then start it too. + if (hasMessageListeners()) + { + startDispatcherIfNecessary(); + } + } + + void startDispatcherIfNecessary() + { + //If we are the dispatcher then we don't need to check we are started + if (Thread.currentThread() == _dispatcher) + { + return; + } + + // If IMMEDIATE_PREFETCH is not set then we need to start fetching + // This is final per session so will be multi-thread safe. + if (!_immediatePrefetch) + { + // We do this now if this is the first call on a started connection + if (isSuspended() && _startedAtLeastOnce.get() && _firstDispatcher.getAndSet(false)) + { + try + { + suspendChannel(false); + } + catch (AMQException e) + { + _logger.info("Unsuspending channel threw an exception:" + e); + } + } + } + + startDispatcherIfNecessary(false); + } + + synchronized void startDispatcherIfNecessary(boolean initiallyStopped) + { + if (_dispatcher == null) + { + _dispatcher = new Dispatcher(); + _dispatcher.setDaemon(true); + _dispatcher.setConnectionStopped(initiallyStopped); + _dispatcher.start(); + } + else + { + _dispatcher.setConnectionStopped(initiallyStopped); + } + } + + void stop() throws AMQException + { + // Stop the server delivering messages to this session. + suspendChannel(true); + + if (_dispatcher != null) + { + _dispatcher.setConnectionStopped(true); + } + } + + /* + * Binds the named queue, with the specified routing key, to the named exchange. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param queueName The name of the queue to bind. + * @param routingKey The routing key to bind the queue with. + * @param arguments Additional arguments. + * @param exchangeName The exchange to bind the queue on. + * + * @throws AMQException If the queue cannot be bound for any reason. + */ + /*private void bindQueue(AMQDestination amqd, AMQShortString queueName, AMQProtocolHandler protocolHandler, FieldTable ft) + throws AMQException, FailoverException + { + AMQFrame queueBind = + QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), ft, // arguments + amqd.getExchangeName(), // exchange + false, // nowait + queueName, // queue + amqd.getRoutingKey(), // routingKey + getTicket()); // ticket + + protocolHandler.syncWrite(queueBind, QueueBindOkBody.class); + }*/ + + private void checkNotTransacted() throws JMSException + { + if (getTransacted()) + { + throw new IllegalStateException("Session is transacted"); + } + } + + private void checkTemporaryDestination(Destination destination) throws JMSException + { + if ((destination instanceof TemporaryDestination)) + { + _logger.debug("destination is temporary"); + final TemporaryDestination tempDest = (TemporaryDestination) destination; + if (tempDest.getSession() != this) + { + _logger.debug("destination is on different session"); + throw new JMSException("Cannot consume from a temporary destination created onanother session"); + } + + if (tempDest.isDeleted()) + { + _logger.debug("destination is deleted"); + throw new JMSException("Cannot consume from a deleted destination"); + } + } + } + + protected void checkTransacted() throws JMSException + { + if (!getTransacted()) + { + throw new IllegalStateException("Session is not transacted"); + } + } + + private void checkValidDestination(Destination destination) throws InvalidDestinationException + { + if (destination == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Queue"); + } + } + + private void checkValidQueue(Queue queue) throws InvalidDestinationException + { + if (queue == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Queue"); + } + } + + /* + * I could have combined the last 3 methods, but this way it improves readability + */ + protected AMQTopic checkValidTopic(Topic topic) throws JMSException + { + if (topic == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Topic"); + } + + if ((topic instanceof TemporaryDestination) && (((TemporaryDestination) topic).getSession() != this)) + { + throw new javax.jms.InvalidDestinationException( + "Cannot create a subscription on a temporary topic created in another session"); + } + + if (!(topic instanceof AMQTopic)) + { + throw new javax.jms.InvalidDestinationException( + "Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: " + + topic.getClass().getName()); + } + + return (AMQTopic) topic; + } + + /** + * Called to close message consumers cleanly. This may or may not be as a result of an error. + * + * @param error not null if this is a result of an error occurring at the connection level + */ + private void closeConsumers(Throwable error) throws JMSException + { + // we need to clone the list of consumers since the close() method updates the _consumers collection + // which would result in a concurrent modification exception + final ArrayList clonedConsumers = new ArrayList(_consumers.values()); + + final Iterator it = clonedConsumers.iterator(); + while (it.hasNext()) + { + final C con = it.next(); + if (error != null) + { + con.notifyError(error); + } + else + { + con.close(false); + } + } + // at this point the _consumers map will be empty + if (_dispatcher != null) + { + _dispatcher.close(); + _dispatcher = null; + } + } + + /** + * Called to close message producers cleanly. This may or may not be as a result of an error. There is + * currently no way of propagating errors to message producers (this is a JMS limitation). + */ + private void closeProducers() throws JMSException + { + // we need to clone the list of producers since the close() method updates the _producers collection + // which would result in a concurrent modification exception + final ArrayList clonedProducers = new ArrayList(_producers.values()); + + final Iterator it = clonedProducers.iterator(); + while (it.hasNext()) + { + final P prod = (P) it.next(); + prod.close(); + } + // at this point the _producers map is empty + } + + /** + * Close all producers or consumers. This is called either in the error case or when closing the session normally. + * + * @param amqe the exception, may be null to indicate no error has occurred + */ + private void closeProducersAndConsumers(AMQException amqe) throws JMSException + { + JMSException jmse = null; + try + { + closeProducers(); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + jmse = e; + } + + try + { + closeConsumers(amqe); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + if (jmse == null) + { + jmse = e; + } + } + + if (jmse != null) + { + throw jmse; + } + } + + /** + * Register to consume from the queue. + * + * @param queueName + */ + private void consumeFromQueue(C consumer, AMQShortString queueName, + AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException + { + int tagId = _nextTag++; + + consumer.setConsumerTag(tagId); + // we must register the consumer in the map before we actually start listening + _consumers.put(tagId, consumer); + + try + { + sendConsume(consumer, queueName, protocolHandler, nowait, messageSelector, tagId); + } + catch (AMQException e) + { + // clean-up the map in the event of an error + _consumers.remove(tagId); + throw e; + } + } + + public abstract void sendConsume(C consumer, AMQShortString queueName, + AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector, int tag) throws AMQException, FailoverException; + + private P createProducerImpl(Destination destination, boolean mandatory, boolean immediate) + throws JMSException + { + return createProducerImpl(destination, mandatory, immediate, false); + } + + private P createProducerImpl(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent) throws JMSException + { + return new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public P execute() throws JMSException, FailoverException + { + checkNotClosed(); + long producerId = getNextProducerId(); + P producer = createMessageProducer(destination, mandatory, + immediate, waitUntilSent, producerId); + registerProducer(producerId, producer); + + return producer; + } + }, _connection).execute(); + } + + public abstract P createMessageProducer(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent, long producerId); + + private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException + { + declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait); + } + + /** + * Returns the number of messages currently queued for the given destination. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param amqd The destination to be checked + * + * @return the number of queued messages. + * + * @throws AMQException If the queue cannot be declared for any reason. + */ + public long getQueueDepth(final AMQDestination amqd) + throws AMQException + { + return new FailoverNoopSupport( + new FailoverProtectedOperation() + { + public Long execute() throws AMQException, FailoverException + { + return requestQueueDepth(amqd); + } + }, _connection).execute(); + + } + + protected abstract Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException; + + /** + * Declares the named exchange and type of exchange. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param name The name of the exchange to declare. + * @param type The type of the exchange to declare. + * @param protocolHandler The protocol handler to process the communication through. + * @param nowait + * + * @throws AMQException If the exchange cannot be declared for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + private void declareExchange(final AMQShortString name, final AMQShortString type, + final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException + { + new FailoverNoopSupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + sendExchangeDeclare(name, type, protocolHandler, nowait); + return null; + } + }, _connection).execute(); + } + + public abstract void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, final AMQProtocolHandler protocolHandler, + final boolean nowait) throws AMQException, FailoverException; + + /** + * Declares a queue for a JMS destination. + * + *

Note that for queues but not topics the name is generated in the client rather than the server. This allows + * the name to be reused on failover if required. In general, the destination indicates whether it wants a name + * generated or not. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param amqd The destination to declare as a queue. + * @param protocolHandler The protocol handler to communicate through. + * + * @return The name of the decalred queue. This is useful where the broker is generating a queue name on behalf of + * the client. + * + * @throws AMQException If the queue cannot be declared for any reason. + * @todo Verify the destiation is valid or throw an exception. + * @todo Be aware of possible changes to parameter order as versions change. + */ + protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean noLocal) + throws AMQException + { + /*return new FailoverRetrySupport(*/ + return new FailoverNoopSupport( + new FailoverProtectedOperation() + { + public AMQShortString execute() throws AMQException, FailoverException + { + // Generate the queue name if the destination indicates that a client generated name is to be used. + if (amqd.isNameRequired()) + { + amqd.setQueueName(protocolHandler.generateQueueName()); + } + + sendQueueDeclare(amqd, protocolHandler); + + return amqd.getAMQQueueName(); + } + }, _connection).execute(); + } + + public abstract void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException; + + /** + * Undeclares the specified queue. + * + *

Note that this operation automatically retries in the event of fail-over. + * + * @param queueName The name of the queue to delete. + * + * @throws JMSException If the queue could not be deleted for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + protected void deleteQueue(final AMQShortString queueName) throws JMSException + { + try + { + new FailoverRetrySupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + sendQueueDelete(queueName); + return null; + } + }, _connection).execute(); + } + catch (AMQException e) + { + throw new JMSAMQException("The queue deletion failed: " + e.getMessage(), e); + } + } + + public abstract void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException; + + private long getNextProducerId() + { + return ++_nextProducerId; + } + + protected AMQProtocolHandler getProtocolHandler() + { + return _connection.getProtocolHandler(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolHandler().getProtocolMajorVersion(); + } + + public byte getProtocolMinorVersion() + { + return getProtocolHandler().getProtocolMinorVersion(); + } + + protected boolean hasMessageListeners() + { + return _hasMessageListeners; + } + + private void markClosedConsumers() throws JMSException + { + if (_dispatcher != null) + { + _dispatcher.close(); + _dispatcher = null; + } + // we need to clone the list of consumers since the close() method updates the _consumers collection + // which would result in a concurrent modification exception + final ArrayList clonedConsumers = new ArrayList(_consumers.values()); + + final Iterator it = clonedConsumers.iterator(); + while (it.hasNext()) + { + final C con = it.next(); + con.markClosed(); + } + // at this point the _consumers map will be empty + } + + private void markClosedProducersAndConsumers() + { + try + { + // no need for a markClosed* method in this case since there is no protocol traffic closing a producer + closeProducers(); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + } + + try + { + markClosedConsumers(); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + } + } + + /** + * Callers must hold the failover mutex before calling this method. + * + * @param consumer + * + * @throws AMQException + */ + private void registerConsumer(C consumer, boolean nowait) throws AMQException // , FailoverException + { + AMQDestination amqd = consumer.getDestination(); + + AMQProtocolHandler protocolHandler = getProtocolHandler(); + + declareExchange(amqd, protocolHandler, false); + + AMQShortString queueName = declareQueue(amqd, protocolHandler, consumer.isNoLocal()); + + // store the consumer queue name + consumer.setQueuename(queueName); + + bindQueue(queueName, amqd.getRoutingKey(), consumer.getArguments(), amqd.getExchangeName(), amqd); + + // If IMMEDIATE_PREFETCH is not required then suspsend the channel to delay prefetch + if (!_immediatePrefetch) + { + // The dispatcher will be null if we have just created this session + // so suspend the channel before we register our consumer so that we don't + // start prefetching until a receive/mListener is set. + if (_dispatcher == null) + { + if (!isSuspended()) + { + try + { + suspendChannel(true); + _logger.info( + "Prefetching delayed existing messages will not flow until requested via receive*() or setML()."); + } + catch (AMQException e) + { + _logger.info("Suspending channel threw an exception:" + e); + } + } + } + } + else + { + _logger.info("Immediately prefetching existing messages to new consumer."); + } + + try + { + consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer.getMessageSelector()); + } + catch (JMSException e) // thrown by getMessageSelector + { + throw new AMQException(null, e.getMessage(), e); + } + catch (FailoverException e) + { + throw new AMQException(null, "Fail-over exception interrupted basic consume.", e); + } + } + + private void registerProducer(long producerId, MessageProducer producer) + { + _producers.put(new Long(producerId), producer); + } + + private void rejectAllMessages(boolean requeue) + { + rejectMessagesForConsumerTag(0, requeue, true); + } + + /** + * @param consumerTag The consumerTag to prune from queue or all if null + * @param requeue Should the removed messages be requeued (or discarded. Possibly to DLQ) + * @param rejectAllConsumers + */ + + private void rejectMessagesForConsumerTag(int consumerTag, boolean requeue, boolean rejectAllConsumers) + { + Iterator messages = _queue.iterator(); + if (_logger.isInfoEnabled()) + { + _logger.info("Rejecting messages from _queue for Consumer tag(" + consumerTag + ") (PDispatchQ) requeue:" + + requeue); + + if (messages.hasNext()) + { + _logger.info("Checking all messages in _queue for Consumer tag(" + consumerTag + ")"); + } + else + { + _logger.info("No messages in _queue to reject"); + } + } + while (messages.hasNext()) + { + UnprocessedMessage message = (UnprocessedMessage) messages.next(); + + if (rejectAllConsumers || (message.getConsumerTag() == consumerTag)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:" + + message.getDeliveryTag()); + } + + messages.remove(); + + rejectMessage(message, requeue); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejected the message(" + message.toString() + ") for consumer :" + consumerTag); + } + } + } + } + + private void resubscribeConsumers() throws AMQException + { + ArrayList consumers = new ArrayList(_consumers.values()); + _consumers.clear(); + + for (C consumer : consumers) + { + consumer.failedOver(); + registerConsumer(consumer, true); + } + } + + private void resubscribeProducers() throws AMQException + { + ArrayList producers = new ArrayList(_producers.values()); + _logger.info(MessageFormat.format("Resubscribing producers = {0} producers.size={1}", producers, producers.size())); // FIXME: removeKey + for (Iterator it = producers.iterator(); it.hasNext();) + { + P producer = (P) it.next(); + producer.resubscribe(); + } + } + + /** + * Suspends or unsuspends this session. + * + * @param suspend true indicates that the session should be suspended, false indicates that it + * should be unsuspended. + * + * @throws AMQException If the session cannot be suspended for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + protected void suspendChannel(boolean suspend) throws AMQException // , FailoverException + { + synchronized (_suspensionLock) + { + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Setting channel flow : " + (suspend ? "suspended" : "unsuspended")); + } + + _suspended = suspend; + sendSuspendChannel(suspend); + } + catch (FailoverException e) + { + throw new AMQException(null, "Fail-over interrupted suspend/unsuspend channel.", e); + } + } + } + + public abstract void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException; + + Object getMessageDeliveryLock() + { + return _messageDeliveryLock; + } + + /** + * Indicates whether this session consumers pre-fetche messages + * + * @return true if this session consumers pre-fetche messages false otherwise + */ + public boolean prefetch() + { + return getAMQConnection().getMaxPrefetch() > 0; + } + + /** Signifies that the session has pending sends to commit. */ + public void markDirty() + { + _dirty = true; + } + + /** Signifies that the session has no pending sends to commit. */ + public void markClean() + { + _dirty = false; + _failedOverDirty = false; + } + + /** + * Check to see if failover has occured since the last call to markClean(commit or rollback). + * + * @return boolean true if failover has occured. + */ + public boolean hasFailedOver() + { + return _failedOverDirty; + } + + /** + * Check to see if any message have been sent in this transaction and have not been commited. + * + * @return boolean true if a message has been sent but not commited + */ + public boolean isDirty() + { + return _dirty; + } + + public void setTicket(int ticket) + { + _ticket = ticket; + } + + public void setFlowControl(final boolean active) + { + _flowControl.setFlowControl(active); + } + + public void checkFlowControl() throws InterruptedException + { + synchronized (_flowControl) + { + while (!_flowControl.getFlowControl()) + { + _flowControl.wait(); + } + } + + } + + /** Used for debugging in the dispatcher. */ + private static final Logger _dispatcherLogger = LoggerFactory.getLogger("org.apache.qpid.client.AMQSession.Dispatcher"); + + /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ + class Dispatcher extends Thread + { + + /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */ + private final AtomicBoolean _closed = new AtomicBoolean(false); + + private final Object _lock = new Object(); + private String dispatcherID = "" + System.identityHashCode(this); + + + + public Dispatcher() + { + super("Dispatcher-Channel-" + _channelId); + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " created"); + } + } + + public void close() + { + _closed.set(true); + interrupt(); + + // fixme awaitTermination + + } + + public void rejectPending(C consumer) + { + synchronized (_lock) + { + boolean stopped = _dispatcher.connectionStopped(); + + if (!stopped) + { + _dispatcher.setConnectionStopped(true); + } + + // Reject messages on pre-receive queue + consumer.rollbackPendingMessages(); + + // Reject messages on pre-dispatch queue + rejectMessagesForConsumerTag(consumer.getConsumerTag(), true, false); + //Let the dispatcher deal with this when it gets to them. + + // closeConsumer + consumer.markClosed(); + + _dispatcher.setConnectionStopped(stopped); + + } + } + + public void rollback() + { + + synchronized (_lock) + { + boolean isStopped = connectionStopped(); + + if (!isStopped) + { + setConnectionStopped(true); + } + + _rollbackMark.set(_highestDeliveryTag.get()); + + _dispatcherLogger.debug("Session Pre Dispatch Queue cleared"); + + for (C consumer : _consumers.values()) + { + if (!consumer.isNoConsume()) + { + consumer.rollback(); + } + else + { + // should perhaps clear the _SQ here. + // consumer._synchronousQueue.clear(); + consumer.clearReceiveQueue(); + } + + } + + for (int i = 0; i < _removedConsumers.size(); i++) + { + // Sends acknowledgement to server + _removedConsumers.get(i).rollback(); + _removedConsumers.remove(i); + } + + setConnectionStopped(isStopped); + } + + } + + public void run() + { + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " started"); + } + + UnprocessedMessage message; + + // Allow disptacher to start stopped + synchronized (_lock) + { + while (!_closed.get() && connectionStopped()) + { + try + { + _lock.wait(); + } + catch (InterruptedException e) + { + // ignore + } + } + } + + try + { + while (!_closed.get() && ((message = (UnprocessedMessage) _queue.take()) != null)) + { + long deliveryTag = message.getDeliveryTag(); + + synchronized (_lock) + { + + while (connectionStopped()) + { + _lock.wait(); + } + + if (!(message instanceof CloseConsumerMessage) + && tagLE(deliveryTag, _rollbackMark.get())) + { + rejectMessage(message, true); + } + else + { + synchronized (_messageDeliveryLock) + { + dispatchMessage(message); + } + } + } + + long current = _rollbackMark.get(); + if (updateRollbackMark(current, deliveryTag)) + { + _rollbackMark.compareAndSet(current, deliveryTag); + } + } + } + catch (InterruptedException e) + { + // ignore + } + + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " thread terminating for channel " + _channelId); + } + } + + // only call while holding lock + final boolean connectionStopped() + { + return _connectionStopped; + } + + boolean setConnectionStopped(boolean connectionStopped) + { + boolean currently; + synchronized (_lock) + { + currently = _connectionStopped; + _connectionStopped = connectionStopped; + _lock.notify(); + + if (_dispatcherLogger.isDebugEnabled()) + { + _dispatcherLogger.debug("Set Dispatcher Connection " + (connectionStopped ? "Stopped" : "Started") + + ": Currently " + (currently ? "Stopped" : "Started")); + } + } + + return currently; + } + + private void dispatchMessage(UnprocessedMessage message) + { + //This if block is not needed anymore as bounce messages are handled separately + //if (message.getDeliverBody() != null) + //{ + final C consumer = + _consumers.get(message.getConsumerTag()); + + if ((consumer == null) || consumer.isClosed()) + { + if (_dispatcherLogger.isInfoEnabled()) + { + if (consumer == null) + { + _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliveryTag() + "] from queue " + + message.getConsumerTag() + " )without a handler - rejecting(requeue)..."); + } + else + { + if (consumer.isNoConsume()) + { + _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliveryTag() + "] from queue " + " consumer(" + + message.getConsumerTag() + ") is closed and a browser so dropping..."); + //DROP MESSAGE + return; + + } + else + { + _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliveryTag() + "] from queue " + " consumer(" + + message.getConsumerTag() + ") is closed rejecting(requeue)..."); + } + } + } + // Don't reject if we're already closing + if (!_closed.get()) + { + rejectMessage(message, true); + } + } + else + { + consumer.notifyMessage(message); + } + + } + } + + protected abstract boolean tagLE(long tag1, long tag2); + + protected abstract boolean updateRollbackMark(long current, long deliveryTag); + + public abstract AMQMessageDelegateFactory getMessageDelegateFactory(); + + /*public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write, + boolean read) throws AMQException + { + getProtocolHandler().writeCommandFrameAndWaitForReply(AccessRequestBody.createAMQFrame(getChannelId(), + getProtocolMajorVersion(), getProtocolMinorVersion(), active, exclusive, passive, read, realm, write), + new BlockingMethodFrameListener(_channelId) + { + + public boolean processMethod(int channelId, AMQMethodBody frame) // throws AMQException + { + if (frame instanceof AccessRequestOkBody) + { + setTicket(((AccessRequestOkBody) frame).getTicket()); + + return true; + } + else + { + return false; + } + } + }); + }*/ + + private class SuspenderRunner implements Runnable + { + private AtomicBoolean _suspend; + + public SuspenderRunner(AtomicBoolean suspend) + { + _suspend = suspend; + } + + public void run() + { + try + { + synchronized (_suspensionLock) + { + suspendChannel(_suspend.get()); + } + } + catch (AMQException e) + { + _logger.warn("Unable to suspend channel"); + } + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java new file mode 100644 index 0000000000..7e257e0c20 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java @@ -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. + * + */ +package org.apache.qpid.client; + +public interface AMQSessionAdapter +{ + public AMQSession getSession(); +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java new file mode 100644 index 0000000000..a1b240ed54 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSessionDirtyException.java @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQSessionDirtyException represents all failures to send data on a transacted session that is + * no longer in a state that the client expects. i.e. failover has occured so previously sent messages + * will not be part of the transaction. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent attempt to perform additional sends on a dirty session. + *
+ */ +public class AMQSessionDirtyException extends AMQException +{ + public AMQSessionDirtyException(String msg) + { + super(AMQConstant.RESOURCE_ERROR, msg, null); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java new file mode 100644 index 0000000000..ab983aa842 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -0,0 +1,850 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverNoopSupport; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.message.MessageFactoryRegistry; +import org.apache.qpid.client.message.FiledTableSupport; +import org.apache.qpid.client.message.AMQMessageDelegateFactory; +import org.apache.qpid.client.message.UnprocessedMessage_0_10; +import org.apache.qpid.util.Serial; +import org.apache.qpid.transport.ExecutionException; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageCreditUnit; +import org.apache.qpid.transport.MessageFlowMode; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.RangeSet; +import org.apache.qpid.transport.Option; +import org.apache.qpid.transport.ExchangeBoundResult; +import org.apache.qpid.transport.Future; +import org.apache.qpid.transport.Range; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionException; +import org.apache.qpid.transport.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.qpid.transport.Option.*; + +import javax.jms.*; +import javax.jms.IllegalStateException; + +import java.util.HashMap; +import java.util.UUID; +import java.util.Map; + +/** + * This is a 0.10 Session + */ +public class AMQSession_0_10 extends AMQSession + implements SessionListener +{ + + /** + * This class logger + */ + private static final Logger _logger = LoggerFactory.getLogger(AMQSession_0_10.class); + + + /** + * The underlying QpidSession + */ + private Session _qpidSession; + + /** + * The latest qpid Exception that has been reaised. + */ + private Object _currentExceptionLock = new Object(); + private SessionException _currentException; + + // a ref on the qpid connection + protected org.apache.qpid.transport.Connection _qpidConnection; + + private RangeSet unacked = new RangeSet(); + private int unackedCount = 0; + + /** + * USed to store the range of in tx messages + */ + private RangeSet _txRangeSet = new RangeSet(); + private int _txSize = 0; + //--- constructors + + /** + * Creates a new session on a connection. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param messageFactoryRegistry The message factory factory for the session. + * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. + * @param qpidConnection The qpid connection + */ + AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, + boolean transacted, int acknowledgeMode, MessageFactoryRegistry messageFactoryRegistry, + int defaultPrefetchHighMark, int defaultPrefetchLowMark) + { + + super(con, channelId, transacted, acknowledgeMode, messageFactoryRegistry, defaultPrefetchHighMark, + defaultPrefetchLowMark); + _qpidConnection = qpidConnection; + _qpidSession = _qpidConnection.createSession(1); + _qpidSession.setSessionListener(this); + if (_transacted) + { + _qpidSession.txSelect(); + } + } + + /** + * Creates a new session on a connection with the default 0-10 message factory. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. + * @param qpidConnection The connection + */ + AMQSession_0_10(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, + boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, int defaultPrefetchLow) + { + + this(qpidConnection, con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), + defaultPrefetchHigh, defaultPrefetchLow); + } + + private void addUnacked(int id) + { + unacked.add(id); + unackedCount++; + } + + private void clearUnacked() + { + unacked.clear(); + unackedCount = 0; + } + + //------- overwritten methods of class AMQSession + + /** + * Acknowledge one or many messages. + * + * @param deliveryTag The tag of the last message to be acknowledged. + * @param multiple true to acknowledge all messages up to and including the one specified by the + * delivery tag, false to just acknowledge that message. + */ + + public void acknowledgeMessage(long deliveryTag, boolean multiple) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Sending ack for delivery tag " + deliveryTag + " on session " + _channelId); + } + // acknowledge this message + if (multiple) + { + for (Long messageTag : _unacknowledgedMessageTags) + { + if( messageTag <= deliveryTag ) + { + addUnacked(messageTag.intValue()); + _unacknowledgedMessageTags.remove(messageTag); + } + } + //empty the list of unack messages + + } + else + { + addUnacked((int) deliveryTag); + _unacknowledgedMessageTags.remove(deliveryTag); + } + + long prefetch = getAMQConnection().getMaxPrefetch(); + + if (unackedCount >= prefetch/2) + { + flushAcknowledgments(); + } + } + + void flushAcknowledgments() + { + if (unackedCount > 0) + { + messageAcknowledge + (unacked, _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); + clearUnacked(); + } + } + + void messageAcknowledge(RangeSet ranges, boolean accept) + { + Session ssn = getQpidSession(); + for (Range range : ranges) + { + ssn.processed(range); + } + ssn.flushProcessed(accept ? BATCH : NONE); + if (accept) + { + ssn.messageAccept(ranges); + } + } + + /** + * Bind a queue with an exchange. + * + * @param queueName Specifies the name of the queue to bind. If the queue name is empty, + * refers to the current + * queue for the session, which is the last declared queue. + * @param exchangeName The exchange name. + * @param routingKey Specifies the routing key for the binding. + * @param arguments 0_8 specific + */ + public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, + final FieldTable arguments, final AMQShortString exchangeName, final AMQDestination destination) + throws AMQException, FailoverException + { + Map args = FiledTableSupport.convertToMap(arguments); + // this is there only becasue the broker may expect a value for x-match + if( ! args.containsKey("x-match") ) + { + args.put("x-match", "any"); + } + + for (AMQShortString rk: destination.getBindingKeys()) + { + _logger.debug("Binding queue : " + queueName.toString() + " exchange: " + exchangeName.toString() + " using binding key " + rk.asString()); + getQpidSession().exchangeBind(queueName.toString(), exchangeName.toString(), rk.toString(), args); + } + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + + /** + * Close this session. + * + * @param timeout no used / 0_8 specific + * @throws AMQException + * @throws FailoverException + */ + public void sendClose(long timeout) throws AMQException, FailoverException + { + flushAcknowledgments(); + getQpidSession().sync(); + getQpidSession().close(); + getCurrentException(); + } + + + /** + * Commit the receipt and the delivery of all messages exchanged by this session resources. + */ + public void sendCommit() throws AMQException, FailoverException + { + getQpidSession().setAutoSync(true); + try + { + getQpidSession().txCommit(); + } + finally + { + getQpidSession().setAutoSync(false); + } + // We need to sync so that we get notify of an error. + getCurrentException(); + } + + /** + * Create a queue with a given name. + * + * @param name The queue name + * @param autoDelete If this field is set and the exclusive field is also set, + * then the queue is deleted when the connection closes. + * @param durable If set when creating a new queue, + * the queue will be marked as durable. + * @param exclusive Exclusive queues can only be used from one connection at a time. + * @param arguments Exclusive queues can only be used from one connection at a time. + * @throws AMQException + * @throws FailoverException + */ + public void sendCreateQueue(AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive, Map arguments) throws AMQException, FailoverException + { + getQpidSession().queueDeclare(name.toString(), null, arguments, durable ? Option.DURABLE : Option.NONE, + autoDelete ? Option.AUTO_DELETE : Option.NONE, + exclusive ? Option.EXCLUSIVE : Option.NONE); + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + /** + * This method asks the broker to redeliver all unacknowledged messages + * + * @throws AMQException + * @throws FailoverException + */ + public void sendRecover() throws AMQException, FailoverException + { + // release all unack messages + RangeSet ranges = new RangeSet(); + while (true) + { + Long tag = _unacknowledgedMessageTags.poll(); + if (tag == null) + { + break; + } + ranges.add((int) (long) tag); + } + getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + public void releaseForRollback() + { + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + getQpidSession().messageRelease(_txRangeSet, Option.SET_REDELIVERED); + _txRangeSet.clear(); + _txSize = 0; + } + + /** + * Release (0_8 notion of Reject) an acquired message + * + * @param deliveryTag the message ID + * @param requeue always true + */ + public void rejectMessage(long deliveryTag, boolean requeue) + { + // The value of requeue is always true + RangeSet ranges = new RangeSet(); + ranges.add((int) deliveryTag); + getQpidSession().messageRelease(ranges, Option.SET_REDELIVERED); + //I don't think we need to sync + } + + /** + * Create an 0_10 message consumer + */ + public BasicMessageConsumer_0_10 createMessageConsumer(final AMQDestination destination, final int prefetchHigh, + final int prefetchLow, final boolean noLocal, + final boolean exclusive, String messageSelector, + final FieldTable ft, final boolean noConsume, + final boolean autoClose) throws JMSException + { + + final AMQProtocolHandler protocolHandler = getProtocolHandler(); + return new BasicMessageConsumer_0_10(_channelId, _connection, destination, messageSelector, noLocal, + _messageFactoryRegistry, this, protocolHandler, ft, prefetchHigh, + prefetchLow, exclusive, _acknowledgeMode, noConsume, autoClose); + } + + /** + * Bind a queue with an exchange. + */ + + public boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey) + throws JMSException + { + return isQueueBound(exchangeName,queueName,routingKey,null); + } + + public boolean isQueueBound(final AMQDestination destination) throws JMSException + { + return isQueueBound(destination.getExchangeName(),destination.getAMQQueueName(),destination.getRoutingKey(),destination.getBindingKeys()); + } + + public boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey,AMQShortString[] bindingKeys) + throws JMSException + { + String rk = ""; + boolean res; + if (bindingKeys != null && bindingKeys.length>0) + { + rk = bindingKeys[0].toString(); + } + else if (routingKey != null) + { + rk = routingKey.toString(); + } + + ExchangeBoundResult bindingQueryResult = + getQpidSession().exchangeBound(exchangeName.toString(),queueName.toString(), rk, null).get(); + + if (rk == null) + { + res = !(bindingQueryResult.getExchangeNotFound() || bindingQueryResult.getQueueNotFound()); + } + else + { + res = !(bindingQueryResult.getKeyNotMatched() || bindingQueryResult.getQueueNotFound() || bindingQueryResult + .getQueueNotMatched()); + } + return res; + } + + /** + * This method is invoked when a consumer is creted + * Registers the consumer with the broker + */ + public void sendConsume(BasicMessageConsumer_0_10 consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, + boolean nowait, String messageSelector, int tag) + throws AMQException, FailoverException + { + boolean preAcquire; + try + { + preAcquire = ( ! consumer.isNoConsume() && + (consumer.getMessageSelector() == null || consumer.getMessageSelector().equals("")) ) + || !(consumer.getDestination() instanceof AMQQueue); + getQpidSession().messageSubscribe + (queueName.toString(), String.valueOf(tag), + getAcknowledgeMode() == NO_ACKNOWLEDGE ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, + preAcquire ? MessageAcquireMode.PRE_ACQUIRED : MessageAcquireMode.NOT_ACQUIRED, null, 0, null, + consumer.isExclusive() ? Option.EXCLUSIVE : Option.NONE); + } + catch (JMSException e) + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, "problem when registering consumer", e); + } + + String consumerTag = ((BasicMessageConsumer_0_10)consumer).getConsumerTagString(); + + if (! prefetch()) + { + getQpidSession().messageSetFlowMode(consumerTag, MessageFlowMode.CREDIT); + } + else + { + getQpidSession().messageSetFlowMode(consumerTag, MessageFlowMode.WINDOW); + } + getQpidSession().messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF); + // We need to sync so that we get notify of an error. + // only if not immediat prefetch + if(prefetch() && (consumer.isStrated() || _immediatePrefetch)) + { + // set the flow + getQpidSession().messageFlow(consumerTag, + MessageCreditUnit.MESSAGE, + getAMQConnection().getMaxPrefetch()); + } + getQpidSession().sync(); + getCurrentException(); + } + + /** + * Create an 0_10 message producer + */ + public BasicMessageProducer_0_10 createMessageProducer(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent, + long producerId) + { + return new BasicMessageProducer_0_10(_connection, (AMQDestination) destination, _transacted, _channelId, this, + getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + + } + + /** + * creates an exchange if it does not already exist + */ + public void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, + final AMQProtocolHandler protocolHandler, final boolean nowait) + throws AMQException, FailoverException + { + getQpidSession().exchangeDeclare(name.toString(), type.toString(), null, null); + // autoDelete --> false + // durable --> false + // passive -- false + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + /** + * Declare a queue with the given queueName + */ + public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) + throws AMQException, FailoverException + { + // do nothing this is only used by 0_8 + } + + /** + * Declare a queue with the given queueName + */ + public AMQShortString send0_10QueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean noLocal) + throws AMQException, FailoverException + { + AMQShortString res; + if (amqd.getAMQQueueName() == null) + { + // generate a name for this queue + res = new AMQShortString("TempQueue" + UUID.randomUUID()); + } + else + { + res = amqd.getAMQQueueName(); + } + Map arguments = null; + if (noLocal) + { + arguments = new HashMap(); + arguments.put("no-local", true); + } + getQpidSession().queueDeclare(res.toString(), null, arguments, + amqd.isAutoDelete() ? Option.AUTO_DELETE : Option.NONE, + amqd.isDurable() ? Option.DURABLE : Option.NONE, + !amqd.isDurable() && amqd.isExclusive() ? Option.EXCLUSIVE : Option.NONE); + // passive --> false + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + return res; + } + + /** + * deletes a queue + */ + public void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException + { + getQpidSession().queueDelete(queueName.toString()); + // ifEmpty --> false + // ifUnused --> false + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + /** + * Activate/deactivate the message flow for all the consumers of this session. + */ + public void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException + { + if (suspend) + { + for (BasicMessageConsumer consumer : _consumers.values()) + { + getQpidSession().messageStop(String.valueOf(consumer.getConsumerTag())); + } + } + else + { + for (BasicMessageConsumer_0_10 consumer : _consumers.values()) + { + String consumerTag = String.valueOf(consumer.getConsumerTag()); + //only set if msg list is null + try + { + if (! prefetch()) + { + if (consumer.getMessageListener() != null) + { + getQpidSession().messageFlow(consumerTag, + MessageCreditUnit.MESSAGE, 1); + } + } + else + { + getQpidSession() + .messageFlow(consumerTag, MessageCreditUnit.MESSAGE, + getAMQConnection().getMaxPrefetch()); + } + getQpidSession() + .messageFlow(consumerTag, MessageCreditUnit.BYTE, 0xFFFFFFFF); + } + catch (Exception e) + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, "Error while trying to get the listener", e); + } + } + } + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + + public void sendRollback() throws AMQException, FailoverException + { + getQpidSession().txRollback(); + // We need to sync so that we get notify of an error. + getQpidSession().sync(); + getCurrentException(); + } + + //------ Private methods + /** + * Access to the underlying Qpid Session + * + * @return The associated Qpid Session. + */ + protected Session getQpidSession() + { + return _qpidSession; + } + + + /** + * Get the latest thrown exception. + * + * @throws org.apache.qpid.AMQException get the latest thrown error. + */ + public void getCurrentException() throws AMQException + { + synchronized (_currentExceptionLock) + { + if (_currentException != null) + { + SessionException se = _currentException; + _currentException = null; + ExecutionException ee = se.getException(); + int code; + if (ee == null) + { + code = 0; + } + else + { + code = ee.getErrorCode().getValue(); + } + throw new AMQException + (AMQConstant.getConstant(code), se.getMessage(), se); + } + } + } + + public void opened(Session ssn) {} + + public void message(Session ssn, MessageTransfer xfr) + { + messageReceived(new UnprocessedMessage_0_10(xfr)); + } + + public void exception(Session ssn, SessionException exc) + { + synchronized (_currentExceptionLock) + { + _currentException = exc; + } + } + + public void closed(Session ssn) {} + + protected AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler, + final boolean noLocal) + throws AMQException + { + /*return new FailoverRetrySupport(*/ + return new FailoverNoopSupport( + new FailoverProtectedOperation() + { + public AMQShortString execute() throws AMQException, FailoverException + { + // Generate the queue name if the destination indicates that a client generated name is to be used. + if (amqd.isNameRequired()) + { + String binddingKey = ""; + for(AMQShortString key : amqd.getBindingKeys()) + { + binddingKey = binddingKey + "_" + key.toString(); + } + amqd.setQueueName(new AMQShortString( binddingKey + "@" + + amqd.getExchangeName().toString() + "_" + UUID.randomUUID())); + } + return send0_10QueueDeclare(amqd, protocolHandler, noLocal); + } + }, _connection).execute(); + } + + + void start() throws AMQException + { + super.start(); + for(BasicMessageConsumer c: _consumers.values()) + { + c.start(); + } + } + + + void stop() throws AMQException + { + super.stop(); + for(BasicMessageConsumer c: _consumers.values()) + { + c.stop(); + } + } + + + + + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException + { + + checkNotClosed(); + AMQTopic origTopic=checkValidTopic(topic); + AMQTopic dest=AMQTopic.createDurable010Topic(origTopic, name, _connection); + + TopicSubscriberAdaptor subscriber=_subscriptions.get(name); + if (subscriber != null) + { + if (subscriber.getTopic().equals(topic)) + { + throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + + name); + } + else + { + unsubscribe(name); + } + } + else + { + AMQShortString topicName; + if (topic instanceof AMQTopic) + { + topicName=((AMQTopic) topic).getBindingKeys()[0]; + } + else + { + topicName=new AMQShortString(topic.getTopicName()); + } + + if (_strictAMQP) + { + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); + } + else + { + _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " + + "for creation durableSubscriber. Requesting queue deletion regardless."); + } + + deleteQueue(dest.getAMQQueueName()); + } + else + { + // if the queue is bound to the exchange but NOT for this topic, then the JMS spec + // says we must trash the subscription. + if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) + && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) + { + deleteQueue(dest.getAMQQueueName()); + } + } + } + + subscriber=new TopicSubscriberAdaptor(dest, createExclusiveConsumer(dest)); + + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); + + return subscriber; + } + + protected Long requestQueueDepth(AMQDestination amqd) + { + return getQpidSession().queueQuery(amqd.getQueueName()).get().getMessageCount(); + } + + + /** + * Store non committed messages for this session + * With 0.10 messages are consumed with window mode, we must send a completion + * before the window size is reached so credits don't dry up. + * @param id + */ + @Override protected void addDeliveredMessage(long id) + { + _txRangeSet.add((int) id); + _txSize++; + // this is a heuristic, we may want to have that configurable + if (_connection.getMaxPrefetch() == 1 || + _connection.getMaxPrefetch() != 0 && _txSize % (_connection.getMaxPrefetch() / 2) == 0) + { + // send completed so consumer credits don't dry up + messageAcknowledge(_txRangeSet, false); + } + } + + @Override public void commit() throws JMSException + { + checkTransacted(); + try + { + if( _txSize > 0 ) + { + messageAcknowledge(_txRangeSet, true); + _txRangeSet.clear(); + _txSize = 0; + } + sendCommit(); + } + catch (AMQException e) + { + throw new JMSAMQException("Failed to commit: " + e.getMessage(), e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e); + } + } + + protected final boolean tagLE(long tag1, long tag2) + { + return Serial.le((int) tag1, (int) tag2); + } + + protected final boolean updateRollbackMark(long currentMark, long deliveryTag) + { + return Serial.lt((int) currentMark, (int) deliveryTag); + } + + public AMQMessageDelegateFactory getMessageDelegateFactory() + { + return AMQMessageDelegateFactory.FACTORY_0_10; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java new file mode 100644 index 0000000000..e5fc36c389 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_8.java @@ -0,0 +1,565 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + + +import javax.jms.*; +import javax.jms.IllegalStateException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; +import org.apache.qpid.client.message.*; +import org.apache.qpid.client.message.AMQMessageDelegateFactory; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodRegistry_0_9; +import org.apache.qpid.jms.Session; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public final class AMQSession_0_8 extends AMQSession +{ + + /** Used for debugging. */ + private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); + + /** + * Creates a new session on a connection. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param messageFactoryRegistry The message factory factory for the session. + * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. + */ + AMQSession_0_8(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, + MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetchHighMark, int defaultPrefetchLowMark) + { + + super(con,channelId,transacted,acknowledgeMode,messageFactoryRegistry,defaultPrefetchHighMark,defaultPrefetchLowMark); + } + + /** + * Creates a new session on a connection with the default message factory factory. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. + */ + AMQSession_0_8(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, + int defaultPrefetchLow) + { + this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, + defaultPrefetchLow); + } + + private ProtocolVersion getProtocolVersion() + { + return getProtocolHandler().getProtocolVersion(); + } + + public void acknowledgeMessage(long deliveryTag, boolean multiple) + { + BasicAckBody body = getMethodRegistry().createBasicAckBody(deliveryTag, multiple); + + final AMQFrame ackFrame = body.generateFrame(_channelId); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Sending ack for delivery tag " + deliveryTag + " on channel " + _channelId); + } + + getProtocolHandler().writeFrame(ackFrame); + _unacknowledgedMessageTags.remove(deliveryTag); + } + + public void sendQueueBind(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, + final AMQShortString exchangeName, final AMQDestination dest) throws AMQException, FailoverException + { + getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createQueueBindBody + (getTicket(),queueName,exchangeName,routingKey,false,arguments). + generateFrame(_channelId), QueueBindOkBody.class); + } + + public void sendClose(long timeout) throws AMQException, FailoverException + { + getProtocolHandler().closeSession(this); + getProtocolHandler().syncWrite(getProtocolHandler().getMethodRegistry().createChannelCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), + new AMQShortString("JMS client closing channel"), 0, 0).generateFrame(_channelId), + ChannelCloseOkBody.class, timeout); + // When control resumes at this point, a reply will have been received that + // indicates the broker has closed the channel successfully. + } + + public void sendCommit() throws AMQException, FailoverException + { + final AMQProtocolHandler handler = getProtocolHandler(); + + handler.syncWrite(getProtocolHandler().getMethodRegistry().createTxCommitBody().generateFrame(_channelId), TxCommitOkBody.class); + } + + public void sendCreateQueue(AMQShortString name, final boolean autoDelete, final boolean durable, final boolean exclusive, final Map arguments) throws AMQException, + FailoverException + { + FieldTable table = null; + if(arguments != null && !arguments.isEmpty()) + { + table = new FieldTable(); + for(Map.Entry entry : arguments.entrySet()) + { + table.setObject(entry.getKey(), entry.getValue()); + } + } + QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,table); + AMQFrame queueDeclare = body.generateFrame(_channelId); + getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class); + } + + public void sendRecover() throws AMQException, FailoverException + { + _unacknowledgedMessageTags.clear(); + + if (isStrictAMQP()) + { + // We can't use the BasicRecoverBody-OK method as it isn't part of the spec. + + BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false); + _connection.getProtocolHandler().writeFrame(body.generateFrame(_channelId)); + _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order."); + } + else + { + // in Qpid the 0-8 spec was hacked to have a recover-ok method... this is bad + // in 0-9 we used the cleaner addition of a new sync recover method with its own ok + if(getProtocolHandler().getProtocolVersion().equals(ProtocolVersion.v8_0)) + { + BasicRecoverBody body = getMethodRegistry().createBasicRecoverBody(false); + _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverOkBody.class); + } + else if(getProtocolVersion().equals(ProtocolVersion.v0_9)) + { + BasicRecoverSyncBody body = ((MethodRegistry_0_9)getMethodRegistry()).createBasicRecoverSyncBody(false); + _connection.getProtocolHandler().syncWrite(body.generateFrame(_channelId), BasicRecoverSyncOkBody.class); + } + else + { + throw new RuntimeException("Unsupported version of the AMQP Protocol: " + getProtocolVersion()); + } + } + } + + public void releaseForRollback() + { + while (true) + { + Long tag = _deliveredMessageTags.poll(); + if (tag == null) + { + break; + } + + rejectMessage(tag, true); + } + + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + } + + public void rejectMessage(long deliveryTag, boolean requeue) + { + if ((_acknowledgeMode == CLIENT_ACKNOWLEDGE) || (_acknowledgeMode == SESSION_TRANSACTED)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting delivery tag:" + deliveryTag + ":SessionHC:" + this.hashCode()); + } + + BasicRejectBody body = getMethodRegistry().createBasicRejectBody(deliveryTag, requeue); + AMQFrame frame = body.generateFrame(_channelId); + + _connection.getProtocolHandler().writeFrame(frame); + } + } + + public boolean isQueueBound(final AMQDestination destination) throws JMSException + { + return isQueueBound(destination.getExchangeName(),destination.getAMQQueueName(),destination.getAMQQueueName()); + } + + + public boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey) + throws JMSException + { + try + { + AMQMethodEvent response = new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public AMQMethodEvent execute() throws AMQException, FailoverException + { + AMQFrame boundFrame = getProtocolHandler().getMethodRegistry().createExchangeBoundBody + (exchangeName, routingKey, queueName).generateFrame(_channelId); + + return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); + + } + }, _connection).execute(); + + // Extract and return the response code from the query. + ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); + + return (responseBody.getReplyCode() == 0); + } + catch (AMQException e) + { + throw new JMSAMQException("Queue bound query failed: " + e.getMessage(), e); + } + } + + @Override public void sendConsume(BasicMessageConsumer_0_8 consumer, + AMQShortString queueName, + AMQProtocolHandler protocolHandler, + boolean nowait, + String messageSelector, + int tag) throws AMQException, FailoverException + { + FieldTable arguments = FieldTableFactory.newFieldTable(); + if ((messageSelector != null) && !messageSelector.equals("")) + { + arguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector); + } + + if (consumer.isAutoClose()) + { + arguments.put(AMQPFilterTypes.AUTO_CLOSE.getValue(), Boolean.TRUE); + } + + if (consumer.isNoConsume()) + { + arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE); + } + + BasicConsumeBody body = getMethodRegistry().createBasicConsumeBody(getTicket(), + queueName, + new AMQShortString(String.valueOf(tag)), + consumer.isNoLocal(), + consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, + consumer.isExclusive(), + nowait, + arguments); + + + AMQFrame jmsConsume = body.generateFrame(_channelId); + + if (nowait) + { + protocolHandler.writeFrame(jmsConsume); + } + else + { + protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class); + } + } + + public void sendExchangeDeclare(final AMQShortString name, final AMQShortString type, final AMQProtocolHandler protocolHandler, + final boolean nowait) throws AMQException, FailoverException + { + ExchangeDeclareBody body = getMethodRegistry().createExchangeDeclareBody(getTicket(),name,type,false,false,false,false,nowait,null); + AMQFrame exchangeDeclare = body.generateFrame(_channelId); + + protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + } + + public void sendQueueDeclare(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) throws AMQException, FailoverException + { + QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),amqd.getAMQQueueName(),false,amqd.isDurable(),amqd.isExclusive(),amqd.isAutoDelete(),false,null); + + AMQFrame queueDeclare = body.generateFrame(_channelId); + + protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class); + } + + public void sendQueueDelete(final AMQShortString queueName) throws AMQException, FailoverException + { + QueueDeleteBody body = getMethodRegistry().createQueueDeleteBody(getTicket(), + queueName, + false, + false, + true); + AMQFrame queueDeleteFrame = body.generateFrame(_channelId); + + getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); + } + + public void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException + { + ChannelFlowBody body = getMethodRegistry().createChannelFlowBody(!suspend); + AMQFrame channelFlowFrame = body.generateFrame(_channelId); + _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class); + } + + public BasicMessageConsumer_0_8 createMessageConsumer(final AMQDestination destination, final int prefetchHigh, + final int prefetchLow, final boolean noLocal, final boolean exclusive, String messageSelector, final FieldTable arguments, + final boolean noConsume, final boolean autoClose) throws JMSException + { + + final AMQProtocolHandler protocolHandler = getProtocolHandler(); + return new BasicMessageConsumer_0_8(_channelId, _connection, destination, messageSelector, noLocal, + _messageFactoryRegistry,this, protocolHandler, arguments, prefetchHigh, prefetchLow, + exclusive, _acknowledgeMode, noConsume, autoClose); + } + + + public BasicMessageProducer_0_8 createMessageProducer(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent, long producerId) + { + + return new BasicMessageProducer_0_8(_connection, (AMQDestination) destination, _transacted, _channelId, + this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + } + + + @Override public void messageReceived(UnprocessedMessage message) + { + + if (message instanceof ReturnMessage) + { + // Return of the bounced message. + returnBouncedMessage((ReturnMessage) message); + } + else + { + super.messageReceived(message); + } + } + + private void returnBouncedMessage(final ReturnMessage msg) + { + _connection.performConnectionTask(new Runnable() + { + public void run() + { + try + { + // Bounced message is processed here, away from the mina thread + AbstractJMSMessage bouncedMessage = + _messageFactoryRegistry.createMessage(0, false, msg.getExchange(), + msg.getRoutingKey(), msg.getContentHeader(), msg.getBodies()); + AMQConstant errorCode = AMQConstant.getConstant(msg.getReplyCode()); + AMQShortString reason = msg.getReplyText(); + _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")"); + + // @TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions. + if (errorCode == AMQConstant.NO_CONSUMERS) + { + _connection.exceptionReceived(new AMQNoConsumersException("Error: " + reason, bouncedMessage, null)); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + _connection.exceptionReceived(new AMQNoRouteException("Error: " + reason, bouncedMessage, null)); + } + else + { + _connection.exceptionReceived( + new AMQUndeliveredException(errorCode, "Error: " + reason, bouncedMessage, null)); + } + + } + catch (Exception e) + { + _logger.error( + "Caught exception trying to raise undelivered message exception (dump follows) - ignoring...", + e); + } + } + }); + } + + + + + public void sendRollback() throws AMQException, FailoverException + { + TxRollbackBody body = getMethodRegistry().createTxRollbackBody(); + AMQFrame frame = body.generateFrame(getChannelId()); + getProtocolHandler().syncWrite(frame, TxRollbackOkBody.class); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException + { + + checkNotClosed(); + AMQTopic origTopic = checkValidTopic(topic); + AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) + { + if (subscriber.getTopic().equals(topic)) + { + throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + + name); + } + else + { + unsubscribe(name); + } + } + else + { + AMQShortString topicName; + if (topic instanceof AMQTopic) + { + topicName = ((AMQTopic) topic).getRoutingKey(); + } + else + { + topicName = new AMQShortString(topic.getTopicName()); + } + + if (_strictAMQP) + { + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); + } + else + { + _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " + + "for creation durableSubscriber. Requesting queue deletion regardless."); + } + + deleteQueue(dest.getAMQQueueName()); + } + else + { + // if the queue is bound to the exchange but NOT for this topic, then the JMS spec + // says we must trash the subscription. + if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) + && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) + { + deleteQueue(dest.getAMQQueueName()); + } + } + } + + subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); + + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); + + return subscriber; + } + + + + + public void setPrefetchLimits(final int messagePrefetch, final long sizePrefetch) throws AMQException + { + new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + + BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(sizePrefetch, messagePrefetch, false); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + getProtocolHandler().syncWrite(basicQosBody.generateFrame(getChannelId()), BasicQosOkBody.class); + + return null; + } + }, _connection).execute(); + } + + class QueueDeclareOkHandler extends SpecificMethodFrameListener + { + + private long _messageCount; + private long _consumerCount; + + public QueueDeclareOkHandler() + { + super(getChannelId(), QueueDeclareOkBody.class); + } + + public boolean processMethod(int channelId, AMQMethodBody frame) //throws AMQException + { + boolean matches = super.processMethod(channelId, frame); + if (matches) + { + QueueDeclareOkBody declareOk = (QueueDeclareOkBody) frame; + _messageCount = declareOk.getMessageCount(); + _consumerCount = declareOk.getConsumerCount(); + } + return matches; + } + + } + + protected Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException + { + AMQFrame queueDeclare = + getMethodRegistry().createQueueDeclareBody(getTicket(), + amqd.getAMQQueueName(), + true, + amqd.isDurable(), + amqd.isExclusive(), + amqd.isAutoDelete(), + false, + null).generateFrame(_channelId); + QueueDeclareOkHandler okHandler = new QueueDeclareOkHandler(); + getProtocolHandler().writeCommandFrameAndWaitForReply(queueDeclare, okHandler); + return okHandler._messageCount; + } + + protected final boolean tagLE(long tag1, long tag2) + { + return tag1 <= tag2; + } + + protected final boolean updateRollbackMark(long currentMark, long deliveryTag) + { + return false; + } + + public AMQMessageDelegateFactory getMessageDelegateFactory() + { + return AMQMessageDelegateFactory.FACTORY_0_8; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java new file mode 100644 index 0000000000..f54cb782c8 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.JMSException; +import javax.jms.TemporaryQueue; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.Random; +import java.util.UUID; + +/** AMQ implementation of a TemporaryQueue. */ +final class AMQTemporaryQueue extends AMQQueue implements TemporaryQueue, TemporaryDestination +{ + + + private final AMQSession _session; + private boolean _deleted; + + /** Create a new instance of an AMQTemporaryQueue */ + public AMQTemporaryQueue(AMQSession session) + { + super(session.getTemporaryQueueExchangeName(), new AMQShortString("TempQueue" + UUID.randomUUID()), true); + _session = session; + } + + /** @see javax.jms.TemporaryQueue#delete() */ + public synchronized void delete() throws JMSException + { + if (_session.hasConsumer(this)) + { + throw new JMSException("Temporary Queue has consumers so cannot be deleted"); + } + _deleted = true; + + // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted + // by the server when there are no more subscriptions to that queue. This is probably not + // quite right for JMSCompliance. + } + + public AMQSession getSession() + { + return _session; + } + + public boolean isDeleted() + { + return _deleted; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java new file mode 100644 index 0000000000..7b5781530b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.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.client; + +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.JMSException; +import javax.jms.TemporaryTopic; +import java.util.UUID; + +/** + * AMQ implementation of TemporaryTopic. + */ +class AMQTemporaryTopic extends AMQTopic implements TemporaryTopic, TemporaryDestination +{ + + private final AMQSession _session; + private boolean _deleted; + /** + * Create new temporary topic. + */ + public AMQTemporaryTopic(AMQSession session) + { + super(session.getTemporaryTopicExchangeName(),new AMQShortString("tmp_" + UUID.randomUUID())); + _session = session; + } + + /** + * @see javax.jms.TemporaryTopic#delete() + */ + public void delete() throws JMSException + { + if(_session.hasConsumer(this)) + { + throw new JMSException("Temporary Topic has consumers so cannot be deleted"); + } + + _deleted = true; + // Currently TemporaryQueue is set to be auto-delete which means that the queue will be deleted + // by the server when there are no more subscriptions to that queue. This is probably not + // quite right for JMSCompliance. + } + + public AMQSession getSession() + { + return _session; + } + + public boolean isDeleted() + { + return _deleted; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java new file mode 100644 index 0000000000..40041afdc6 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.JMSException; +import javax.jms.Topic; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.BindingURL; + +public class AMQTopic extends AMQDestination implements Topic +{ + /** + * Constructor for use in creating a topic using a BindingURL. + * + * @param binding The binding url object. + */ + public AMQTopic(BindingURL binding) + { + super(binding); + } + +// public AMQTopic(String exchangeName, String routingKey) +// { +// this(new AMQShortString(exchangeName), new AMQShortString(routingKey)); +// } + + public AMQTopic(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName) + { + super(exchange, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, routingKey, true, true, queueName, false); + } + + public AMQTopic(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName,AMQShortString[] bindingKeys) + { + super(exchange, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, routingKey, true, true, queueName, false,bindingKeys); + } + + public AMQTopic(AMQConnection conn, String routingKey) + { + this(conn.getDefaultTopicExchangeName(), new AMQShortString(routingKey)); + } + + + public AMQTopic(AMQShortString exchangeName, String routingKey) + { + this(exchangeName, new AMQShortString(routingKey)); + } + + public AMQTopic(AMQShortString exchangeName, AMQShortString routingKey) + { + this(exchangeName, routingKey, null); + } + + public AMQTopic(AMQShortString exchangeName, AMQShortString name, boolean isAutoDelete, AMQShortString queueName, boolean isDurable) + { + super(exchangeName, ExchangeDefaults.TOPIC_EXCHANGE_CLASS, name, true, isAutoDelete, + queueName, isDurable); + } + + protected AMQTopic(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, boolean isDurable) + { + super(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, isDurable ); + } + + protected AMQTopic(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString routingKey, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, boolean isDurable,AMQShortString[] bindingKeys) + { + super(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, isDurable,bindingKeys); + } + + public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection) + throws JMSException + { + return new AMQTopic(topic.getExchangeName(), topic.getRoutingKey(), false, + getDurableTopicQueueName(subscriptionName, connection), + true); + } + + public static AMQTopic createDurable010Topic(AMQTopic topic, String subscriptionName, AMQConnection connection) + throws JMSException + { + return new AMQTopic(topic.getExchangeName(), ExchangeDefaults.TOPIC_EXCHANGE_CLASS, topic.getRoutingKey(), true, false, + getDurableTopicQueueName(subscriptionName, connection), true); + } + + public static AMQShortString getDurableTopicQueueName(String subscriptionName, AMQConnection connection) throws JMSException + { + return new AMQShortString(connection.getClientID() + ":" + subscriptionName); + } + + public String getTopicName() throws JMSException + { + return super.getRoutingKey().toString(); + } + + public AMQShortString getRoutingKey() + { + return super.getRoutingKey(); + } + + public boolean isNameRequired() + { + return !isDurable(); + } + + /** + * Override since the queue is always private and we must ensure it remains null. If not, + * reuse of the topic when registering consumers will make all consumers listen on the same (private) queue rather + * than getting their own (private) queue. + *

+ * This is relatively nasty but it is difficult to come up with a more elegant solution, given + * the requirement in the case on AMQQueue and possibly other AMQDestination subclasses to + * use the underlying queue name even where it is server generated. + */ + public void setQueueName(String queueName) + { + } + + public boolean equals(Object o) + { + return (o instanceof AMQTopic) + && ((AMQTopic)o).getExchangeName().equals(getExchangeName()) + && ((AMQTopic)o).getRoutingKey().equals(getRoutingKey()); + + } + + public int hashCode() + { + return getExchangeName().hashCode() + getRoutingKey().hashCode(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java new file mode 100644 index 0000000000..f44f8414fa --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java @@ -0,0 +1,226 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.io.Serializable; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +public class AMQTopicSessionAdaptor implements TopicSession, AMQSessionAdapter +{ + protected final AMQSession _session; + + public AMQTopicSessionAdaptor(Session session) + { + _session = (AMQSession) session; + } + + public Topic createTopic(String string) throws JMSException + { + return _session.createTopic(string); + } + + public TopicSubscriber createSubscriber(Topic topic) throws JMSException + { + return _session.createSubscriber(topic); + } + + public TopicSubscriber createSubscriber(Topic topic, String string, boolean b) throws JMSException + { + return _session.createSubscriber(topic, string, b); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String string) throws JMSException + { + return _session.createDurableSubscriber(topic, string); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String string, String string1, boolean b) throws JMSException + { + return _session.createDurableSubscriber(topic, string, string1, b); + } + + public TopicPublisher createPublisher(Topic topic) throws JMSException + { + return _session.createPublisher(topic); + } + + public TemporaryTopic createTemporaryTopic() throws JMSException + { + return _session.createTemporaryTopic(); + } + + public void unsubscribe(String string) throws JMSException + { + _session.unsubscribe(string); + } + + public BytesMessage createBytesMessage() throws JMSException + { + return _session.createBytesMessage(); + } + + public MapMessage createMapMessage() throws JMSException + { + return _session.createMapMessage(); + } + + public Message createMessage() throws JMSException + { + return _session.createMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException + { + return _session.createObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException + { + return _session.createObjectMessage(); + } + + public StreamMessage createStreamMessage() throws JMSException + { + return _session.createStreamMessage(); + } + + public TextMessage createTextMessage() throws JMSException + { + return _session.createTextMessage(); + } + + public TextMessage createTextMessage(String string) throws JMSException + { + return _session.createTextMessage(string); + } + + public boolean getTransacted() throws JMSException + { + return _session.getTransacted(); + } + + public int getAcknowledgeMode() throws JMSException + { + return _session.getAcknowledgeMode(); + } + + public void commit() throws JMSException + { + _session.commit(); + } + + public void rollback() throws JMSException + { + _session.rollback(); + } + + public void close() throws JMSException + { + _session.close(); + } + + public void recover() throws JMSException + { + _session.recover(); + } + + public MessageListener getMessageListener() throws JMSException + { + return _session.getMessageListener(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException + { + _session.setMessageListener(messageListener); + } + + public void run() + { + _session.run(); + } + + public MessageProducer createProducer(Destination destination) throws JMSException + { + return _session.createProducer(destination); + } + + public MessageConsumer createConsumer(Destination destination) throws JMSException + { + return _session.createConsumer(destination); + } + + public MessageConsumer createConsumer(Destination destination, String string) throws JMSException + { + return _session.createConsumer(destination, string); + } + + public MessageConsumer createConsumer(Destination destination, String string, boolean b) throws JMSException + { + return _session.createConsumer(destination, string, b); + } + + //The following methods cannot be called from a TopicSession as per JMS spec + public Queue createQueue(String string) throws JMSException + { + throw new IllegalStateException("Cannot call createQueue from TopicSession"); + } + + public QueueBrowser createBrowser(Queue queue) throws JMSException + { + throw new IllegalStateException("Cannot call createBrowser from TopicSession"); + } + + public QueueBrowser createBrowser(Queue queue, String string) throws JMSException + { + throw new IllegalStateException("Cannot call createBrowser from TopicSession"); + } + + public TemporaryQueue createTemporaryQueue() throws JMSException + { + throw new IllegalStateException("Cannot call createTemporaryQueue from TopicSession"); + } + + public AMQSession getSession() + { + return _session; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java new file mode 100644 index 0000000000..fa2afb3ee4 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.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.client; + +import org.apache.qpid.framing.AMQShortString; + +public class AMQUndefinedDestination extends AMQDestination +{ + + private static final AMQShortString UNKNOWN_EXCHANGE_CLASS = new AMQShortString("unknown"); + + + public AMQUndefinedDestination(AMQShortString exchange, AMQShortString routingKey, AMQShortString queueName) + { + super(exchange, UNKNOWN_EXCHANGE_CLASS, routingKey, queueName); + } + + public boolean isNameRequired() + { + return getAMQQueueName() == null; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java new file mode 100644 index 0000000000..76422c6297 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -0,0 +1,1079 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.message.*; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.*; +import org.apache.qpid.jms.MessageConsumer; +import org.apache.qpid.jms.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.ArrayList; +import java.util.Collections; +import java.util.TreeSet; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public abstract class BasicMessageConsumer extends Closeable implements MessageConsumer +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class); + + /** The connection being used by this consumer */ + protected final AMQConnection _connection; + + protected final String _messageSelector; + + private final boolean _noLocal; + + private final AMQDestination _destination; + + /** + * When true indicates that a blocking receive call is in progress + */ + private final AtomicBoolean _receiving = new AtomicBoolean(false); + /** + * Holds an atomic reference to the listener installed. + */ + private final AtomicReference _messageListener = new AtomicReference(); + + /** The consumer tag allows us to close the consumer by sending a jmsCancel method to the broker */ + protected int _consumerTag; + + /** We need to know the channel id when constructing frames */ + protected final int _channelId; + + /** + * Used in the blocking receive methods to receive a message from the Session thread.

Or to notify of errors + *

Argument true indicates we want strict FIFO semantics + */ + protected final BlockingQueue _synchronousQueue; + + protected final MessageFactoryRegistry _messageFactory; + + protected final AMQSession _session; + + protected final AMQProtocolHandler _protocolHandler; + + /** + * We need to store the "raw" field table so that we can resubscribe in the event of failover being required + */ + private final FieldTable _arguments; + + /** + * We store the high water prefetch field in order to be able to reuse it when resubscribing in the event of + * failover + */ + private final int _prefetchHigh; + + /** + * We store the low water prefetch field in order to be able to reuse it when resubscribing in the event of + * failover + */ + private final int _prefetchLow; + + /** + * We store the exclusive field in order to be able to reuse it when resubscribing in the event of failover + */ + private final boolean _exclusive; + + /** + * The acknowledge mode in force for this consumer. Note that the AMQP protocol allows different ack modes per + * consumer whereas JMS defines this at the session level, hence why we associate it with the consumer in our + * implementation. + */ + protected final int _acknowledgeMode; + + /** + * Number of messages unacknowledged in DUPS_OK_ACKNOWLEDGE mode + */ + private int _outstanding; + + /** + * Switch to enable sending of acknowledgements when using DUPS_OK_ACKNOWLEDGE mode. Enabled when _outstannding + * number of msgs >= _prefetchHigh and disabled at < _prefetchLow + */ + private boolean _dups_ok_acknowledge_send; + + /** + * List of tags delievered, The last of which which should be acknowledged on commit in transaction mode. + */ + private ConcurrentLinkedQueue _receivedDeliveryTags = new ConcurrentLinkedQueue(); + + /** The last tag that was "multiple" acknowledged on this session (if transacted) */ + private long _lastAcked; + + /** set of tags which have previously been acked; but not part of the multiple ack (transacted mode only) */ + private final SortedSet _previouslyAcked = new TreeSet(); + + private final Object _commitLock = new Object(); + + /** + * The thread that was used to call receive(). This is important for being able to interrupt that thread if a + * receive() is in progress. + */ + private Thread _receivingThread; + + + /** + * Used to store this consumer queue name + * Usefull when more than binding key should be used + */ + private AMQShortString _queuename; + + /** + * autoClose denotes that the consumer will automatically cancel itself when there are no more messages to receive + * on the queue. This is used for queue browsing. + */ + private final boolean _autoClose; + + private final boolean _noConsume; + private List _closedStack = null; + + + + protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination, + String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, + AMQSession session, AMQProtocolHandler protocolHandler, + FieldTable arguments, int prefetchHigh, int prefetchLow, + boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) + { + _channelId = channelId; + _connection = connection; + _messageSelector = messageSelector; + _noLocal = noLocal; + _destination = destination; + _messageFactory = messageFactory; + _session = session; + _protocolHandler = protocolHandler; + _arguments = arguments; + _prefetchHigh = prefetchHigh; + _prefetchLow = prefetchLow; + _exclusive = exclusive; + + _synchronousQueue = new LinkedBlockingQueue(); + _autoClose = autoClose; + _noConsume = noConsume; + + // Force queue browsers not to use acknowledge modes. + if (_noConsume) + { + _acknowledgeMode = Session.NO_ACKNOWLEDGE; + } + else + { + _acknowledgeMode = acknowledgeMode; + } + } + + public AMQDestination getDestination() + { + return _destination; + } + + public String getMessageSelector() throws JMSException + { + checkPreConditions(); + + return _messageSelector; + } + + public MessageListener getMessageListener() throws JMSException + { + checkPreConditions(); + + return _messageListener.get(); + } + + public int getAcknowledgeMode() + { + return _acknowledgeMode; + } + + protected boolean isMessageListenerSet() + { + return _messageListener.get() != null; + } + + public void setMessageListener(final MessageListener messageListener) throws JMSException + { + checkPreConditions(); + + // if the current listener is non-null and the session is not stopped, then + // it is an error to call this method. + + // i.e. it is only valid to call this method if + // + // (a) the connection is stopped, in which case the dispatcher is not running + // OR + // (b) the listener is null AND we are not receiving synchronously at present + // + + if (!_session.getAMQConnection().started()) + { + _messageListener.set(messageListener); + _session.setHasMessageListeners(); + + if (_logger.isDebugEnabled()) + { + _logger.debug( + "Session stopped : Message listener(" + messageListener + ") set for destination " + _destination); + } + } + else + { + if (_receiving.get()) + { + throw new javax.jms.IllegalStateException("Another thread is already receiving synchronously."); + } + + if (!_messageListener.compareAndSet(null, messageListener)) + { + throw new javax.jms.IllegalStateException("Attempt to alter listener while session is started."); + } + + _logger.debug("Message listener set for destination " + _destination); + + if (messageListener != null) + { + //todo: handle case where connection has already been started, and the dispatcher has alreaded started + // putting values on the _synchronousQueue + + synchronized (_session) + { + _messageListener.set(messageListener); + _session.setHasMessageListeners(); + _session.startDispatcherIfNecessary(); + + // If we already have messages on the queue, deliver them to the listener + Object o = _synchronousQueue.poll(); + while (o != null) + { + notifyMessage((AbstractJMSMessage) o); + o = _synchronousQueue.poll(); + } + } + } + } + } + + protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException + { + if (_session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE) + { + _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag()); + } + + _session.setInRecovery(false); + preDeliver(jmsMsg); + } + + /** + * @param immediate if true then return immediately if the connection is failing over + * + * @return boolean if the acquisition was successful + * + * @throws JMSException if a listener has already been set or another thread is receiving + * @throws InterruptedException if interrupted + */ + private boolean acquireReceiving(boolean immediate) throws JMSException, InterruptedException + { + if (_connection.isFailingOver()) + { + if (immediate) + { + return false; + } + else + { + _connection.blockUntilNotFailingOver(); + } + } + + if (!_receiving.compareAndSet(false, true)) + { + throw new javax.jms.IllegalStateException("Another thread is already receiving."); + } + + if (isMessageListenerSet()) + { + throw new javax.jms.IllegalStateException("A listener has already been set."); + } + + _receivingThread = Thread.currentThread(); + return true; + } + + private void releaseReceiving() + { + _receiving.set(false); + _receivingThread = null; + } + + public FieldTable getArguments() + { + return _arguments; + } + + public int getPrefetch() + { + return _prefetchHigh; + } + + public int getPrefetchHigh() + { + return _prefetchHigh; + } + + public int getPrefetchLow() + { + return _prefetchLow; + } + + public boolean isNoLocal() + { + return _noLocal; + } + + public boolean isExclusive() + { + return _exclusive; + } + + public boolean isReceiving() + { + return _receiving.get(); + } + + public Message receive() throws JMSException + { + return receive(0); + } + + public Message receive(long l) throws JMSException + { + + checkPreConditions(); + + try + { + acquireReceiving(false); + } + catch (InterruptedException e) + { + _logger.warn("Interrupted acquire: " + e); + if (isClosed()) + { + return null; + } + } + + _session.startDispatcherIfNecessary(); + + try + { + Object o = getMessageFromQueue(l); + final AbstractJMSMessage m = returnMessageOrThrow(o); + if (m != null) + { + preApplicationProcessing(m); + postDeliver(m); + } + return m; + } + catch (InterruptedException e) + { + _logger.warn("Interrupted: " + e); + + return null; + } + finally + { + releaseReceiving(); + } + } + + public Object getMessageFromQueue(long l) throws InterruptedException + { + Object o; + if (l > 0) + { + o = _synchronousQueue.poll(l, TimeUnit.MILLISECONDS); + } + else if (l < 0) + { + o = _synchronousQueue.poll(); + } + else + { + o = _synchronousQueue.take(); + } + return o; + } + + public Message receiveNoWait() throws JMSException + { + checkPreConditions(); + + try + { + if (!acquireReceiving(true)) + { + //If we couldn't acquire the receiving thread then return null. + // This will occur if failing over. + return null; + } + } + catch (InterruptedException e) + { + /* + * This seems slightly shoddy but should never actually be executed + * since we told acquireReceiving to return immediately and it shouldn't + * block on anything. + */ + + return null; + } + + _session.startDispatcherIfNecessary(); + + try + { + Object o = getMessageFromQueue(-1); + final AbstractJMSMessage m = returnMessageOrThrow(o); + if (m != null) + { + preApplicationProcessing(m); + postDeliver(m); + } + + return m; + } + catch (InterruptedException e) + { + _logger.warn("Interrupted: " + e); + + return null; + } + finally + { + releaseReceiving(); + } + } + + /** + * We can get back either a Message or an exception from the queue. This method examines the argument and deals with + * it by throwing it (if an exception) or returning it (in any other case). + * + * @param o the object to return or throw + * @return a message only if o is a Message + * @throws JMSException if the argument is a throwable. If it is a JMSException it is rethrown as is, but if not a + * JMSException is created with the linked exception set appropriately + */ + private AbstractJMSMessage returnMessageOrThrow(Object o) throws JMSException + { + // errors are passed via the queue too since there is no way of interrupting the poll() via the API. + if (o instanceof Throwable) + { + JMSException e = new JMSException("Message consumer forcibly closed due to error: " + o); + if (o instanceof Exception) + { + e.setLinkedException((Exception) o); + } + + throw e; + } + else if (o instanceof CloseConsumerMessage) + { + _closed.set(true); + deregisterConsumer(); + return null; + } + else + { + return (AbstractJMSMessage) o; + } + } + + public void close() throws JMSException + { + close(true); + } + + public void close(boolean sendClose) throws JMSException + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing consumer:" + debugIdentity()); + } + + if (!_closed.getAndSet(true)) + { + if (_logger.isDebugEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) + { + _logger.debug(_consumerTag + " previously:" + _closedStack.toString()); + } + else + { + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); + } + } + + if (sendClose) + { + // The Synchronized block only needs to protect network traffic. + synchronized (_connection.getFailoverMutex()) + { + try + { + sendCancel(); + } + catch (AMQException e) + { + throw new JMSAMQException("Error closing consumer: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("FailoverException interrupted basic cancel.", e); + } + } + } + else + { + // FIXME: wow this is ugly + // //fixme this probably is not right + // if (!isNoConsume()) + { // done in BasicCancelOK Handler but not sending one so just deregister. + deregisterConsumer(); + } + } + + // This will occur if session.close is called closing all consumers we may be blocked waiting for a receive + // so we need to let it know it is time to close. + if ((_messageListener != null) && _receiving.get()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Interrupting thread: " + _receivingThread); + } + + _receivingThread.interrupt(); + } + } + } + + abstract void sendCancel() throws AMQException, FailoverException; + + /** + * Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has + * vetoed automatic resubscription. The caller must hold the failover mutex. + */ + void markClosed() + { + // synchronized (_closed) + { + _closed.set(true); + + if (_logger.isDebugEnabled()) + { + if (_closedStack != null) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + _logger.debug(_consumerTag + " markClosed():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.debug(_consumerTag + " previously:" + _closedStack.toString()); + } + else + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); + } + } + } + + deregisterConsumer(); + } + + /** + * @param closeMessage + * this message signals that we should close the browser + */ + public void notifyCloseMessage(CloseConsumerMessage closeMessage) + { + if (isMessageListenerSet()) + { + // Currently only possible to get this msg type with a browser. + // If we get the message here then we should probably just close + // this consumer. + // Though an AutoClose consumer with message listener is quite odd.. + // Just log out the fact so we know where we are + _logger.warn("Using an AutoCloseconsumer with message listener is not supported."); + } + else + { + try + { + _synchronousQueue.put(closeMessage); + } + catch (InterruptedException e) + { + _logger.info(" SynchronousQueue.put interupted. Usually result of connection closing," + + "but we shouldn't have close yet"); + } + } + } + + + /** + * Called from the AMQSession when a message has arrived for this consumer. This methods handles both the case of a + * message listener or a synchronous receive() caller. + * + * @param messageFrame the raw unprocessed mesage + */ + void notifyMessage(U messageFrame) + { + if (messageFrame instanceof CloseConsumerMessage) + { + notifyCloseMessage((CloseConsumerMessage) messageFrame); + return; + } + + + + try + { + AbstractJMSMessage jmsMessage = createJMSMessageFromUnprocessedMessage(_session.getMessageDelegateFactory(), messageFrame); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Message is of type: " + jmsMessage.getClass().getName()); + } + // synchronized (_closed) + + { + // if (!_closed.get()) + { + + //preDeliver(jmsMessage); + + notifyMessage(jmsMessage); + } + // else + // { + // _logger.error("MESSAGE REJECTING!"); + // _session.rejectMessage(jmsMessage, true); + // //_logger.error("MESSAGE JUST DROPPED!"); + // } + } + } + catch (Exception e) + { + if (e instanceof InterruptedException) + { + _logger.info("SynchronousQueue.put interupted. Usually result of connection closing"); + } + else + { + _logger.error("Caught exception (dump follows) - ignoring...", e); + } + } + } + + public abstract AbstractJMSMessage createJMSMessageFromUnprocessedMessage(AMQMessageDelegateFactory delegateFactory, U messageFrame) + throws Exception; + + /** @param jmsMessage this message has already been processed so can't redo preDeliver */ + public void notifyMessage(AbstractJMSMessage jmsMessage) + { + try + { + if (isMessageListenerSet()) + { + preApplicationProcessing(jmsMessage); + getMessageListener().onMessage(jmsMessage); + postDeliver(jmsMessage); + } + else + { + // we should not be allowed to add a message is the + // consumer is closed + _synchronousQueue.put(jmsMessage); + } + } + catch (Exception e) + { + if (e instanceof InterruptedException) + { + _logger.info("reNotification : SynchronousQueue.put interupted. Usually result of connection closing"); + } + else + { + _logger.error("reNotification : Caught exception (dump follows) - ignoring...", e); + } + } + } + + void preDeliver(AbstractJMSMessage msg) + { + switch (_acknowledgeMode) + { + + case Session.PRE_ACKNOWLEDGE: + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + break; + + case Session.CLIENT_ACKNOWLEDGE: + // we set the session so that when the user calls acknowledge() it can call the method on session + // to send out the appropriate frame + msg.setAMQSession(_session); + break; + case Session.SESSION_TRANSACTED: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + else + { + _session.addDeliveredMessage(msg.getDeliveryTag()); + } + + break; + } + } + + void postDeliver(AbstractJMSMessage msg) throws JMSException + { + msg.setJMSDestination(_destination); + switch (_acknowledgeMode) + { + + case Session.CLIENT_ACKNOWLEDGE: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + _session.markDirty(); + break; + + case Session.DUPS_OK_ACKNOWLEDGE: + case Session.AUTO_ACKNOWLEDGE: + // we do not auto ack a message if the application code called recover() + if (!_session.isInRecovery()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + + break; + } + } + + + /** + * Acknowledge up to last message delivered (if any). Used when commiting. + * + * @return the lastDeliveryTag to acknowledge + */ + Long getLastDelivered() + { + if (!_receivedDeliveryTags.isEmpty()) + { + Long lastDeliveryTag = _receivedDeliveryTags.poll(); + + while (!_receivedDeliveryTags.isEmpty()) + { + lastDeliveryTag = _receivedDeliveryTags.poll(); + } + + assert _receivedDeliveryTags.isEmpty(); + + return lastDeliveryTag; + } + + return null; + } + + /** + * Acknowledge up to last message delivered (if any). Used when commiting. + */ + void acknowledgeDelivered() + { + synchronized(_commitLock) + { + ArrayList tagsToAck = new ArrayList(); + + while (!_receivedDeliveryTags.isEmpty()) + { + tagsToAck.add(_receivedDeliveryTags.poll()); + } + + Collections.sort(tagsToAck); + + long prevAcked = _lastAcked; + long oldAckPoint = -1; + + while(oldAckPoint != prevAcked) + { + oldAckPoint = prevAcked; + + Iterator tagsToAckIterator = tagsToAck.iterator(); + + while(tagsToAckIterator.hasNext() && tagsToAckIterator.next() == prevAcked+1) + { + tagsToAckIterator.remove(); + prevAcked++; + } + + Iterator previousAckIterator = _previouslyAcked.iterator(); + while(previousAckIterator.hasNext() && previousAckIterator.next() == prevAcked+1) + { + previousAckIterator.remove(); + prevAcked++; + } + + } + if(prevAcked != _lastAcked) + { + _session.acknowledgeMessage(prevAcked, true); + _lastAcked = prevAcked; + } + + Iterator tagsToAckIterator = tagsToAck.iterator(); + + while(tagsToAckIterator.hasNext()) + { + Long tag = tagsToAckIterator.next(); + _session.acknowledgeMessage(tag, false); + _previouslyAcked.add(tag); + } + } + } + + + void notifyError(Throwable cause) + { + // synchronized (_closed) + { + _closed.set(true); + if (_logger.isDebugEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) + { + _logger.debug(_consumerTag + " notifyError():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.debug(_consumerTag + " previously" + _closedStack.toString()); + } + else + { + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); + } + } + } + // QPID-293 can "request redelivery of this error through dispatcher" + + // we have no way of propagating the exception to a message listener - a JMS limitation - so we + // deal with the case where we have a synchronous receive() waiting for a message to arrive + if (!isMessageListenerSet()) + { + // offer only succeeds if there is a thread waiting for an item from the queue + if (_synchronousQueue.offer(cause)) + { + _logger.debug("Passed exception to synchronous queue for propagation to receive()"); + } + } + + deregisterConsumer(); + } + + /** + * Perform cleanup to deregister this consumer. This occurs when closing the consumer in both the clean case and in + * the case of an error occurring. + */ + private void deregisterConsumer() + { + _session.deregisterConsumer(this); + } + + public int getConsumerTag() + { + return _consumerTag; + } + + public void setConsumerTag(int consumerTag) + { + _consumerTag = consumerTag; + } + + public AMQSession getSession() + { + return _session; + } + + private void checkPreConditions() throws JMSException + { + + this.checkNotClosed(); + + if ((_session == null) || _session.isClosed()) + { + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public boolean isNoConsume() + { + return _noConsume; + } + + public void rollback() + { + rollbackPendingMessages(); + } + + public void rollbackPendingMessages() + { + if (_synchronousQueue.size() > 0) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting the messages(" + _synchronousQueue + .size() + ") in _syncQueue (PRQ)" + "for consumer with tag:" + _consumerTag); + } + + Iterator iterator = _synchronousQueue.iterator(); + + int initialSize = _synchronousQueue.size(); + + boolean removed = false; + while (iterator.hasNext()) + { + + Object o = iterator.next(); + if (o instanceof AbstractJMSMessage) + { + _session.rejectMessage(((AbstractJMSMessage) o), true); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejected message:" + ((AbstractJMSMessage) o).getDeliveryTag()); + } + + iterator.remove(); + removed = true; + + } + else + { + _logger.error("Queue contained a :" + o.getClass() + + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); + iterator.remove(); + removed = true; + } + } + + if (removed && (initialSize == _synchronousQueue.size())) + { + _logger.error("Queue had content removed but didn't change in size." + initialSize); + } + + + if (_synchronousQueue.size() != 0) + { + _logger.warn("Queue was not empty after rejecting all messages Remaining:" + _synchronousQueue.size()); + rollback(); + } + + clearReceiveQueue(); + } + } + + public String debugIdentity() + { + return String.valueOf(_consumerTag) + "[" + System.identityHashCode(this) + "]"; + } + + public void clearReceiveQueue() + { + _synchronousQueue.clear(); + } + + public void start() + { + // do nothing as this is a 0_10 feature + } + + + public void stop() + { + // do nothing as this is a 0_10 feature + } + + public boolean isStrated() + { + // do nothing as this is a 0_10 feature + return false; + } + + public AMQShortString getQueuename() + { + return _queuename; + } + + public void setQueuename(AMQShortString queuename) + { + this._queuename = queuename; + } + + public void addBindingKey(AMQDestination amqd, String routingKey) throws AMQException + { + _session.addBindingKey(this,amqd,routingKey); + } + + /** to be called when a failover has occured */ + public void failedOver() + { + clearReceiveQueue(); + // TGM FIXME: think this should just be removed + // clearUnackedMessages(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java new file mode 100644 index 0000000000..7d535643c0 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.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.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.client.message.*; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.transport.*; +import org.apache.qpid.QpidException; +import org.apache.qpid.filter.MessageFilter; +import org.apache.qpid.filter.JMSSelectorFilter; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; +import javax.jms.MessageListener; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * This is a 0.10 message consumer. + */ +public class BasicMessageConsumer_0_10 extends BasicMessageConsumer +{ + + /** + * This class logger + */ + protected final Logger _logger = LoggerFactory.getLogger(getClass()); + + /** + * The message selector filter associated with this consumer message selector + */ + private MessageFilter _filter = null; + + /** + * The underlying QpidSession + */ + private AMQSession_0_10 _0_10session; + + /** + * Indicates whether this consumer receives pre-acquired messages + */ + private boolean _preAcquire = true; + + /** + * Indicate whether this consumer is started. + */ + private boolean _isStarted = false; + + /** + * Specify whether this consumer is performing a sync receive + */ + private final AtomicBoolean _syncReceive = new AtomicBoolean(false); + private String _consumerTagString; + + //--- constructor + protected BasicMessageConsumer_0_10(int channelId, AMQConnection connection, AMQDestination destination, + String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, + AMQSession session, AMQProtocolHandler protocolHandler, + FieldTable arguments, int prefetchHigh, int prefetchLow, + boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) + throws JMSException + { + super(channelId, connection, destination, messageSelector, noLocal, messageFactory, session, protocolHandler, + arguments, prefetchHigh, prefetchLow, exclusive, acknowledgeMode, noConsume, autoClose); + _0_10session = (AMQSession_0_10) session; + if (messageSelector != null && !messageSelector.equals("")) + { + try + { + _filter = new JMSSelectorFilter(messageSelector); + } + catch (QpidException e) + { + throw new InvalidSelectorException("cannot create consumer because of selector issue"); + } + if (destination instanceof AMQQueue) + { + _preAcquire = false; + } + } + _isStarted = connection.started(); + } + + + @Override public void setConsumerTag(int consumerTag) + { + super.setConsumerTag(consumerTag); + _consumerTagString = String.valueOf(consumerTag); + } + + public String getConsumerTagString() + { + return _consumerTagString; + } + + /** + * + * This is invoked by the session thread when emptying the session message queue. + * We first check if the message is valid (match the selector) and then deliver it to the + * message listener or to the sync consumer queue. + * + * @param jmsMessage this message has already been processed so can't redo preDeliver + */ + @Override public void notifyMessage(AbstractJMSMessage jmsMessage) + { + boolean messageOk = false; + try + { + messageOk = checkPreConditions(jmsMessage); + } + catch (AMQException e) + { + _logger.error("Receivecd an Exception when receiving message",e); + try + { + + getSession().getAMQConnection().getExceptionListener() + .onException(new JMSAMQException("Error when receiving message", e)); + } + catch (Exception e1) + { + // we should silently log thie exception as it only hanppens when the connection is closed + _logger.error("Exception when receiving message", e1); + } + } + if (messageOk) + { + if (isMessageListenerSet() && ! getSession().prefetch()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1); + } + _logger.debug("messageOk, trying to notify"); + super.notifyMessage(jmsMessage); + } + } + + //----- overwritten methods + + /** + * This method is invoked when this consumer is stopped. + * It tells the broker to stop delivering messages to this consumer. + */ + @Override void sendCancel() throws AMQException + { + ((AMQSession_0_10) getSession()).getQpidSession().messageCancel(getConsumerTagString()); + ((AMQSession_0_10) getSession()).getQpidSession().sync(); + // confirm cancel + getSession().confirmConsumerCancelled(getConsumerTag()); + ((AMQSession_0_10) getSession()).getCurrentException(); + } + + @Override void notifyMessage(UnprocessedMessage_0_10 messageFrame) + { + + super.notifyMessage(messageFrame); + } + + @Override protected void preApplicationProcessing(AbstractJMSMessage jmsMsg) throws JMSException + { + super.preApplicationProcessing(jmsMsg); + if (!_session.getTransacted() && _session.getAcknowledgeMode() != org.apache.qpid.jms.Session.CLIENT_ACKNOWLEDGE) + { + _session.addUnacknowledgedMessage(jmsMsg.getDeliveryTag()); + } + } + + @Override public AbstractJMSMessage createJMSMessageFromUnprocessedMessage( + AMQMessageDelegateFactory delegateFactory, UnprocessedMessage_0_10 msg) throws Exception + { + AMQMessageDelegate_0_10.updateExchangeTypeMapping(msg.getMessageTransfer().getHeader(), ((AMQSession_0_10)getSession()).getQpidSession()); + return _messageFactory.createMessage(msg.getMessageTransfer()); + } + + // private methods + /** + * Check whether a message can be delivered to this consumer. + * + * @param message The message to be checked. + * @return true if the message matches the selector and can be acquired, false otherwise. + * @throws AMQException If the message preConditions cannot be checked due to some internal error. + */ + private boolean checkPreConditions(AbstractJMSMessage message) throws AMQException + { + boolean messageOk = true; + // TODO Use a tag for fiding out if message filtering is done here or by the broker. + try + { + if (_messageSelector != null && !_messageSelector.equals("")) + { + messageOk = _filter.matches(message); + } + } + catch (Exception e) + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, "Error when evaluating message selector", e); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("messageOk " + messageOk); + _logger.debug("_preAcquire " + _preAcquire); + } + if (!messageOk) + { + if (_preAcquire) + { + // this is the case for topics + // We need to ack this message + if (_logger.isDebugEnabled()) + { + _logger.debug("filterMessage - trying to ack message"); + } + acknowledgeMessage(message); + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Message not OK, releasing"); + } + releaseMessage(message); + } + // if we are syncrhonously waiting for a message + // and messages are not prefetched we then need to request another one + if(! getSession().prefetch()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1); + } + } + // now we need to acquire this message if needed + // this is the case of queue with a message selector set + if (!_preAcquire && messageOk && !isNoConsume()) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("filterMessage - trying to acquire message"); + } + messageOk = acquireMessage(message); + _logger.debug("filterMessage - *************************************"); + _logger.debug("filterMessage - message acquire status : " + messageOk); + _logger.debug("filterMessage - *************************************"); + } + return messageOk; + } + + + /** + * Acknowledge a message + * + * @param message The message to be acknowledged + * @throws AMQException If the message cannot be acquired due to some internal error. + */ + private void acknowledgeMessage(AbstractJMSMessage message) throws AMQException + { + if (!_preAcquire) + { + RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); + _0_10session.messageAcknowledge + (ranges, + _acknowledgeMode != org.apache.qpid.jms.Session.NO_ACKNOWLEDGE); + _0_10session.getCurrentException(); + } + } + + /** + * Release a message + * + * @param message The message to be released + * @throws AMQException If the message cannot be released due to some internal error. + */ + private void releaseMessage(AbstractJMSMessage message) throws AMQException + { + if (_preAcquire) + { + RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); + _0_10session.getQpidSession().messageRelease(ranges); + _0_10session.getCurrentException(); + } + } + + /** + * Acquire a message + * + * @param message The message to be acquired + * @return true if the message has been acquired, false otherwise. + * @throws AMQException If the message cannot be acquired due to some internal error. + */ + private boolean acquireMessage(AbstractJMSMessage message) throws AMQException + { + boolean result = false; + if (!_preAcquire) + { + RangeSet ranges = new RangeSet(); + ranges.add((int) message.getDeliveryTag()); + + Acquired acq = _0_10session.getQpidSession().messageAcquire(ranges).get(); + + RangeSet acquired = acq.getTransfers(); + if (acquired != null && acquired.size() > 0) + { + result = true; + } + } + return result; + } + + + public void setMessageListener(final MessageListener messageListener) throws JMSException + { + super.setMessageListener(messageListener); + if (messageListener != null && ! getSession().prefetch()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1); + } + if (messageListener != null && !_synchronousQueue.isEmpty()) + { + Iterator messages=_synchronousQueue.iterator(); + while (messages.hasNext()) + { + AbstractJMSMessage message=(AbstractJMSMessage) messages.next(); + messages.remove(); + _session.rejectMessage(message, true); + } + } + } + + public boolean isStrated() + { + return _isStarted; + } + + public void start() + { + _isStarted = true; + if (_syncReceive.get()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1); + } + } + + public void stop() + { + _isStarted = false; + } + + /** + * When messages are not prefetched we need to request a message from the + * broker. + * Note that if the timeout is too short a message may be queued in _synchronousQueue until + * this consumer closes or request it. + * @param l + * @return + * @throws InterruptedException + */ + public Object getMessageFromQueue(long l) throws InterruptedException + { + if (isStrated() && ! getSession().prefetch() && _synchronousQueue.isEmpty()) + { + _0_10session.getQpidSession().messageFlow(getConsumerTagString(), + MessageCreditUnit.MESSAGE, 1); + } + if (! getSession().prefetch()) + { + _syncReceive.set(true); + } + Object o = super.getMessageFromQueue(l); + if (! getSession().prefetch()) + { + _syncReceive.set(false); + } + return o; + } + + void postDeliver(AbstractJMSMessage msg) throws JMSException + { + super.postDeliver(msg); + if (_acknowledgeMode == org.apache.qpid.jms.Session.NO_ACKNOWLEDGE && !_session.isInRecovery()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java new file mode 100644 index 0000000000..494a8fb43d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java @@ -0,0 +1,85 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.QpidException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.message.*; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.filter.JMSSelectorFilter; +import org.apache.qpid.framing.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicMessageConsumer_0_8 extends BasicMessageConsumer +{ + protected final Logger _logger = LoggerFactory.getLogger(getClass()); + + protected BasicMessageConsumer_0_8(int channelId, AMQConnection connection, AMQDestination destination, + String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, + AMQProtocolHandler protocolHandler, FieldTable arguments, int prefetchHigh, int prefetchLow, + boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) throws JMSException + { + super(channelId, connection, destination,messageSelector,noLocal,messageFactory,session, + protocolHandler, arguments, prefetchHigh, prefetchLow, exclusive, + acknowledgeMode, noConsume, autoClose); + try + { + + if (messageSelector != null && messageSelector.length() > 0) + { + JMSSelectorFilter _filter = new JMSSelectorFilter(messageSelector); + } + } + catch (QpidException e) + { + throw new InvalidSelectorException("cannot create consumer because of selector issue"); + } + } + + void sendCancel() throws AMQException, FailoverException + { + BasicCancelBody body = getSession().getMethodRegistry().createBasicCancelBody(new AMQShortString(String.valueOf(_consumerTag)), false); + + final AMQFrame cancelFrame = body.generateFrame(_channelId); + + _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class); + + if (_logger.isDebugEnabled()) + { + _logger.debug("CancelOk'd for consumer:" + debugIdentity()); + } + } + + public AbstractJMSMessage createJMSMessageFromUnprocessedMessage(AMQMessageDelegateFactory delegateFactory, UnprocessedMessage_0_8 messageFrame)throws Exception + { + + return _messageFactory.createMessage(messageFrame.getDeliveryTag(), + messageFrame.isRedelivered(), messageFrame.getExchange(), + messageFrame.getRoutingKey(), messageFrame.getContentHeader(), messageFrame.getBodies()); + + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java new file mode 100644 index 0000000000..954a3bc28f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -0,0 +1,561 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import javax.jms.BytesMessage; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.MessageConverter; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.util.UUIDGen; +import org.apache.qpid.util.UUIDs; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class BasicMessageProducer extends Closeable implements org.apache.qpid.jms.MessageProducer +{ + protected final Logger _logger = LoggerFactory.getLogger(getClass()); + + private AMQConnection _connection; + + /** + * If true, messages will not get a timestamp. + */ + protected boolean _disableTimestamps; + + /** + * Priority of messages created by this producer. + */ + private int _messagePriority; + + /** + * Time to live of messages. Specified in milliseconds but AMQ has 1 second resolution. + */ + private long _timeToLive; + + /** + * Delivery mode used for this producer. + */ + private int _deliveryMode = DeliveryMode.PERSISTENT; + + /** + * The Destination used for this consumer, if specified upon creation. + */ + protected AMQDestination _destination; + + /** + * Default encoding used for messages produced by this producer. + */ + private String _encoding; + + /** + * Default encoding used for message produced by this producer. + */ + private String _mimeType; + + protected AMQProtocolHandler _protocolHandler; + + /** + * True if this producer was created from a transacted session + */ + private boolean _transacted; + + protected int _channelId; + + /** + * This is an id generated by the session and is used to tie individual producers to the session. This means we + * can deregister a producer with the session when the producer is clsoed. We need to be able to tie producers + * to the session so that when an error is propagated to the session it can close the producer (meaning that + * a client that happens to hold onto a producer reference will get an error if he tries to use it subsequently). + */ + private long _producerId; + + /** + * The session used to create this producer + */ + protected AMQSession _session; + + private final boolean _immediate; + + private final boolean _mandatory; + + private final boolean _waitUntilSent; + + private boolean _disableMessageId; + + private UUIDGen _messageIdGenerator = UUIDs.newGenerator(); + + protected String _userID; // ref user id used in the connection. + + private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0]; + + protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, + boolean waitUntilSent) + { + _connection = connection; + _destination = destination; + _transacted = transacted; + _protocolHandler = protocolHandler; + _channelId = channelId; + _session = session; + _producerId = producerId; + if (destination != null && !(destination instanceof AMQUndefinedDestination)) + { + declareDestination(destination); + } + + _immediate = immediate; + _mandatory = mandatory; + _waitUntilSent = waitUntilSent; + _userID = connection.getUsername(); + } + + void resubscribe() throws AMQException + { + if (_destination != null && !(_destination instanceof AMQUndefinedDestination)) + { + declareDestination(_destination); + } + } + + abstract void declareDestination(AMQDestination destination); + + public void setDisableMessageID(boolean b) throws JMSException + { + checkPreConditions(); + checkNotClosed(); + _disableMessageId = b; + } + + public boolean getDisableMessageID() throws JMSException + { + checkNotClosed(); + + return _disableMessageId; + } + + public void setDisableMessageTimestamp(boolean b) throws JMSException + { + checkPreConditions(); + _disableTimestamps = b; + } + + public boolean getDisableMessageTimestamp() throws JMSException + { + checkNotClosed(); + + return _disableTimestamps; + } + + public void setDeliveryMode(int i) throws JMSException + { + checkPreConditions(); + if ((i != DeliveryMode.NON_PERSISTENT) && (i != DeliveryMode.PERSISTENT)) + { + throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i + + " is illegal"); + } + + _deliveryMode = i; + } + + public int getDeliveryMode() throws JMSException + { + checkNotClosed(); + + return _deliveryMode; + } + + public void setPriority(int i) throws JMSException + { + checkPreConditions(); + if ((i < 0) || (i > 9)) + { + throw new IllegalArgumentException("Priority of " + i + " is illegal. Value must be in range 0 to 9"); + } + + _messagePriority = i; + } + + public int getPriority() throws JMSException + { + checkNotClosed(); + + return _messagePriority; + } + + public void setTimeToLive(long l) throws JMSException + { + checkPreConditions(); + if (l < 0) + { + throw new IllegalArgumentException("Time to live must be non-negative - supplied value was " + l); + } + + _timeToLive = l; + } + + public long getTimeToLive() throws JMSException + { + checkNotClosed(); + + return _timeToLive; + } + + public Destination getDestination() throws JMSException + { + checkNotClosed(); + + return _destination; + } + + public void close() throws JMSException + { + _closed.set(true); + _session.deregisterProducer(_producerId); + } + + public void send(Message message) throws JMSException + { + checkPreConditions(); + checkInitialDestination(); + + synchronized (_connection.getFailoverMutex()) + { + sendImpl(_destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); + } + } + + public void send(Message message, int deliveryMode) throws JMSException + { + checkPreConditions(); + checkInitialDestination(); + + synchronized (_connection.getFailoverMutex()) + { + sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, _immediate); + } + } + + public void send(Message message, int deliveryMode, boolean immediate) throws JMSException + { + checkPreConditions(); + checkInitialDestination(); + synchronized (_connection.getFailoverMutex()) + { + sendImpl(_destination, message, deliveryMode, _messagePriority, _timeToLive, _mandatory, immediate); + } + } + + public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions(); + checkInitialDestination(); + synchronized (_connection.getFailoverMutex()) + { + sendImpl(_destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate); + } + } + + public void send(Destination destination, Message message) throws JMSException + { + checkPreConditions(); + checkDestination(destination); + synchronized (_connection.getFailoverMutex()) + { + validateDestination(destination); + sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, + _immediate); + } + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException + { + checkPreConditions(); + checkDestination(destination); + synchronized (_connection.getFailoverMutex()) + { + validateDestination(destination); + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, _mandatory, _immediate); + } + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, + boolean mandatory) throws JMSException + { + checkPreConditions(); + checkDestination(destination); + synchronized (_connection.getFailoverMutex()) + { + validateDestination(destination); + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, _immediate); + } + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, + boolean mandatory, boolean immediate) throws JMSException + { + checkPreConditions(); + checkDestination(destination); + synchronized (_connection.getFailoverMutex()) + { + validateDestination(destination); + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate); + } + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, + boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException + { + checkPreConditions(); + checkDestination(destination); + synchronized (_connection.getFailoverMutex()) + { + validateDestination(destination); + sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, + waitUntilSent); + } + } + + private AbstractJMSMessage convertToNativeMessage(Message message) throws JMSException + { + if (message instanceof AbstractJMSMessage) + { + return (AbstractJMSMessage) message; + } + else + { + AbstractJMSMessage newMessage; + + if (message instanceof BytesMessage) + { + newMessage = new MessageConverter(_session, (BytesMessage) message).getConvertedMessage(); + } + else if (message instanceof MapMessage) + { + newMessage = new MessageConverter(_session, (MapMessage) message).getConvertedMessage(); + } + else if (message instanceof ObjectMessage) + { + newMessage = new MessageConverter(_session, (ObjectMessage) message).getConvertedMessage(); + } + else if (message instanceof TextMessage) + { + newMessage = new MessageConverter(_session, (TextMessage) message).getConvertedMessage(); + } + else if (message instanceof StreamMessage) + { + newMessage = new MessageConverter(_session, (StreamMessage) message).getConvertedMessage(); + } + else + { + newMessage = new MessageConverter(_session, message).getConvertedMessage(); + } + + if (newMessage != null) + { + return newMessage; + } + else + { + throw new JMSException("Unable to send message, due to class conversion error: " + + message.getClass().getName()); + } + } + } + + private void validateDestination(Destination destination) throws JMSException + { + if (!(destination instanceof AMQDestination)) + { + throw new JMSException("Unsupported destination class: " + + ((destination != null) ? destination.getClass() : null)); + } + + AMQDestination amqDestination = (AMQDestination) destination; + if(!amqDestination.isExchangeExistsChecked()) + { + declareDestination(amqDestination); + amqDestination.setExchangeExistsChecked(true); + } + } + + protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, + boolean mandatory, boolean immediate) throws JMSException + { + sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent); + } + + /** + * The caller of this method must hold the failover mutex. + * + * @param destination + * @param origMessage + * @param deliveryMode + * @param priority + * @param timeToLive + * @param mandatory + * @param immediate + * + * @throws JMSException + */ + protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive, + boolean mandatory, boolean immediate, boolean wait) throws JMSException + { + checkTemporaryDestination(destination); + origMessage.setJMSDestination(destination); + + AbstractJMSMessage message = convertToNativeMessage(origMessage); + + if (_transacted) + { + if (_session.hasFailedOver() && _session.isDirty()) + { + throw new JMSAMQException("Failover has occurred and session is dirty so unable to send.", + new AMQSessionDirtyException("Failover has occurred and session is dirty " + + "so unable to send.")); + } + } + + UUID messageId = null; + if (_disableMessageId) + { + message.setJMSMessageID((UUID)null); + } + else + { + messageId = _messageIdGenerator.generate(); + message.setJMSMessageID(messageId); + } + + sendMessage(destination, origMessage, message, messageId, deliveryMode, priority, timeToLive, mandatory, immediate, wait); + + if (message != origMessage) + { + _logger.debug("Updating original message"); + origMessage.setJMSPriority(message.getJMSPriority()); + origMessage.setJMSTimestamp(message.getJMSTimestamp()); + _logger.debug("Setting JMSExpiration:" + message.getJMSExpiration()); + origMessage.setJMSExpiration(message.getJMSExpiration()); + origMessage.setJMSMessageID(message.getJMSMessageID()); + } + + if (_transacted) + { + _session.markDirty(); + } + } + + abstract void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, + UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory, + boolean immediate, boolean wait) throws JMSException; + + private void checkTemporaryDestination(AMQDestination destination) throws JMSException + { + if (destination instanceof TemporaryDestination) + { + _logger.debug("destination is temporary destination"); + TemporaryDestination tempDest = (TemporaryDestination) destination; + if (tempDest.getSession().isClosed()) + { + _logger.debug("session is closed"); + throw new JMSException("Session for temporary destination has been closed"); + } + + if (tempDest.isDeleted()) + { + _logger.debug("destination is deleted"); + throw new JMSException("Cannot send to a deleted temporary destination"); + } + } + } + + public void setMimeType(String mimeType) throws JMSException + { + checkNotClosed(); + _mimeType = mimeType; + } + + public void setEncoding(String encoding) throws JMSException, UnsupportedEncodingException + { + checkNotClosed(); + _encoding = encoding; + } + + private void checkPreConditions() throws javax.jms.IllegalStateException, JMSException + { + checkNotClosed(); + + if ((_session == null) || _session.isClosed()) + { + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + + private void checkInitialDestination() + { + if (_destination == null) + { + throw new UnsupportedOperationException("Destination is null"); + } + } + + private void checkDestination(Destination suppliedDestination) throws InvalidDestinationException + { + if ((_destination != null) && (suppliedDestination != null)) + { + throw new UnsupportedOperationException( + "This message producer was created with a Destination, therefore you cannot use an unidentified Destination"); + } + + if (suppliedDestination == null) + { + throw new InvalidDestinationException("Supplied Destination was invalid"); + } + + } + + public AMQSession getSession() + { + return _session; + } + + public boolean isBound(AMQDestination destination) throws JMSException + { + return _session.isQueueBound(destination.getExchangeName(), null, destination.getRoutingKey()); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java new file mode 100644 index 0000000000..4e5077f0cd --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java @@ -0,0 +1,186 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.nio.ByteBuffer; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.DeliveryMode; + +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.FiledTableSupport; +import org.apache.qpid.client.message.AMQMessageDelegate_0_10; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.util.Strings; +import org.apache.qpid.njms.ExceptionHelper; +import org.apache.qpid.transport.*; +import static org.apache.qpid.transport.Option.*; + +/** + * This is a 0_10 message producer. + */ +public class BasicMessageProducer_0_10 extends BasicMessageProducer +{ + private byte[] userIDBytes; + + BasicMessageProducer_0_10(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, + boolean immediate, boolean mandatory, boolean waitUntilSent) + { + super(connection, destination, transacted, channelId, session, protocolHandler, producerId, immediate, + mandatory, waitUntilSent); + + userIDBytes = Strings.toUTF8(_userID); + } + + void declareDestination(AMQDestination destination) + { + ((AMQSession_0_10) getSession()).getQpidSession().exchangeDeclare(destination.getExchangeName().toString(), + destination.getExchangeClass().toString(), + null, null); + } + + //--- Overwritten methods + + /** + * Sends a message to a given destination + */ + void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, + UUID messageId, int deliveryMode, int priority, long timeToLive, boolean mandatory, + boolean immediate, boolean wait) throws JMSException + { + message.prepareForSending(); + + AMQMessageDelegate_0_10 delegate = (AMQMessageDelegate_0_10) message.getDelegate(); + + DeliveryProperties deliveryProp = delegate.getDeliveryProperties(); + MessageProperties messageProps = delegate.getMessageProperties(); + + // On the receiving side, this will be read in to the JMSXUserID as well. + messageProps.setUserId(userIDBytes); + + if (messageId != null) + { + messageProps.setMessageId(messageId); + } + else if (messageProps.hasMessageId()) + { + messageProps.clearMessageId(); + } + + if (!_disableTimestamps) + { + final long currentTime = System.currentTimeMillis(); + deliveryProp.setTimestamp(currentTime); + if (timeToLive > 0) + { + deliveryProp.setExpiration(currentTime + timeToLive); + message.setJMSExpiration(currentTime + timeToLive); + } + else + { + deliveryProp.setExpiration(0); + message.setJMSExpiration(0); + } + message.setJMSTimestamp(currentTime); + } + + if (!deliveryProp.hasDeliveryMode() || deliveryProp.getDeliveryMode().getValue() != deliveryMode) + { + MessageDeliveryMode mode; + switch (deliveryMode) + { + case DeliveryMode.PERSISTENT: + mode = MessageDeliveryMode.PERSISTENT; + break; + case DeliveryMode.NON_PERSISTENT: + mode = MessageDeliveryMode.NON_PERSISTENT; + break; + default: + throw new IllegalArgumentException("illegal delivery mode: " + deliveryMode); + } + deliveryProp.setDeliveryMode(mode); + message.setJMSDeliveryMode(deliveryMode); + } + if (!deliveryProp.hasPriority() || deliveryProp.getPriority().getValue() != priority) + { + deliveryProp.setPriority(MessageDeliveryPriority.get((short) priority)); + message.setJMSPriority(priority); + } + String exchangeName = destination.getExchangeName().toString(); + if ( deliveryProp.getExchange() == null || ! deliveryProp.getExchange().equals(exchangeName)) + { + deliveryProp.setExchange(exchangeName); + } + String routingKey = destination.getRoutingKey().toString(); + if (deliveryProp.getRoutingKey() == null || ! deliveryProp.getRoutingKey().equals(routingKey)) + { + deliveryProp.setRoutingKey(routingKey); + } + + messageProps.setContentLength(message.getContentLength()); + + // send the message + try + { + org.apache.qpid.transport.Session ssn = (org.apache.qpid.transport.Session) + ((AMQSession_0_10) getSession()).getQpidSession(); + + // if true, we need to sync the delivery of this message + boolean sync = (deliveryMode == DeliveryMode.PERSISTENT && + getSession().getAMQConnection().getSyncPersistence()); + + org.apache.mina.common.ByteBuffer data = message.getData(); + ByteBuffer buffer = data == null ? ByteBuffer.allocate(0) : data.buf().slice(); + + ssn.messageTransfer(destination.getExchangeName().toString(), MessageAcceptMode.NONE, + MessageAcquireMode.PRE_ACQUIRED, + new Header(deliveryProp, messageProps), + buffer, sync ? SYNC : NONE); + if (sync) + { + ssn.sync(); + } + + + } + catch (RuntimeException rte) + { + JMSException ex = new JMSException("Exception when sending message"); + rte.printStackTrace(); + ex.setLinkedException(rte); + throw ex; + } + } + + + public boolean isBound(AMQDestination destination) throws JMSException + { + return _session.isQueueBound(destination); + } +} + diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java new file mode 100644 index 0000000000..048065eac9 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_8.java @@ -0,0 +1,207 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.util.UUID; + +import javax.jms.JMSException; +import javax.jms.Message; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.AMQMessageDelegate; +import org.apache.qpid.client.message.AMQMessageDelegate_0_8; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.CompositeAMQDataBlock; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.ExchangeDeclareBody; + +public class BasicMessageProducer_0_8 extends BasicMessageProducer +{ + + BasicMessageProducer_0_8(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, + boolean waitUntilSent) + { + super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent); + } + + void declareDestination(AMQDestination destination) + { + + ExchangeDeclareBody body = getSession().getMethodRegistry().createExchangeDeclareBody(_session.getTicket(), + destination.getExchangeName(), + destination.getExchangeClass(), + false, + false, + false, + false, + true, + null); + // Declare the exchange + // Note that the durable and internal arguments are ignored since passive is set to false + + AMQFrame declare = body.generateFrame(_channelId); + + _protocolHandler.writeFrame(declare); + } + + void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message, + UUID messageId, int deliveryMode,int priority, long timeToLive, boolean mandatory, + boolean immediate, boolean wait) throws JMSException + { + BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(), + destination.getExchangeName(), + destination.getRoutingKey(), + mandatory, + immediate); + + AMQFrame publishFrame = body.generateFrame(_channelId); + + message.prepareForSending(); + ByteBuffer payload = message.getData(); + AMQMessageDelegate_0_8 delegate = (AMQMessageDelegate_0_8) message.getDelegate(); + BasicContentHeaderProperties contentHeaderProperties = delegate.getContentHeaderProperties(); + + contentHeaderProperties.setUserId(_userID); + + if (!_disableTimestamps) + { + final long currentTime = System.currentTimeMillis(); + contentHeaderProperties.setTimestamp(currentTime); + + if (timeToLive > 0) + { + contentHeaderProperties.setExpiration(currentTime + timeToLive); + } + else + { + contentHeaderProperties.setExpiration(0); + } + } + + contentHeaderProperties.setDeliveryMode((byte) deliveryMode); + contentHeaderProperties.setPriority((byte) priority); + + final int size = (payload != null) ? payload.limit() : 0; + final int contentBodyFrameCount = calculateContentBodyFrameCount(payload); + final AMQFrame[] frames = new AMQFrame[2 + contentBodyFrameCount]; + + if (payload != null) + { + createContentBodies(payload, frames, 2, _channelId); + } + + if ((contentBodyFrameCount != 0) && _logger.isDebugEnabled()) + { + _logger.debug("Sending content body frames to " + destination); + } + + + // TODO: This is a hacky way of getting the AMQP class-id for the Basic class + int classIfForBasic = getSession().getMethodRegistry().createBasicQosOkBody().getClazz(); + + AMQFrame contentHeaderFrame = + ContentHeaderBody.createAMQFrame(_channelId, + classIfForBasic, 0, contentHeaderProperties, size); + if (_logger.isDebugEnabled()) + { + _logger.debug("Sending content header frame to " + destination); + } + + frames[0] = publishFrame; + frames[1] = contentHeaderFrame; + CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames); + + try + { + _session.checkFlowControl(); + } + catch (InterruptedException e) + { + JMSException jmsEx = new JMSException("Interrupted while waiting for flow control to be removed"); + jmsEx.setLinkedException(e); + throw jmsEx; + } + + _protocolHandler.writeFrame(compositeFrame, wait); + } + + /** + * Create content bodies. This will split a large message into numerous bodies depending on the negotiated + * maximum frame size. + * + * @param payload + * @param frames + * @param offset + * @param channelId @return the array of content bodies + */ + private void createContentBodies(ByteBuffer payload, AMQFrame[] frames, int offset, int channelId) + { + + if (frames.length == (offset + 1)) + { + frames[offset] = ContentBody.createAMQFrame(channelId, new ContentBody(payload)); + } + else + { + + final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1; + long remaining = payload.remaining(); + for (int i = offset; i < frames.length; i++) + { + payload.position((int) framePayloadMax * (i - offset)); + int length = (remaining >= framePayloadMax) ? (int) framePayloadMax : (int) remaining; + payload.limit(payload.position() + length); + frames[i] = ContentBody.createAMQFrame(channelId, new ContentBody(payload.slice())); + + remaining -= length; + } + } + + } + + private int calculateContentBodyFrameCount(ByteBuffer payload) + { + // we substract one from the total frame maximum size to account for the end of frame marker in a body frame + // (0xCE byte). + int frameCount; + if ((payload == null) || (payload.remaining() == 0)) + { + frameCount = 0; + } + else + { + int dataLength = payload.remaining(); + final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1; + int lastFrame = ((dataLength % framePayloadMax) > 0) ? 1 : 0; + frameCount = (int) (dataLength / framePayloadMax) + lastFrame; + } + + return frameCount; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java new file mode 100644 index 0000000000..7e119343a1 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.IllegalStateException; +import javax.jms.JMSException; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Captures the 'closed' state of an object, that is initially open, can be tested to see if it is closed, and provides + * a 'close' method to close it. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Mark an object as closed. + *
Check if an object is closed. + *
Raise a JMS exception if an object is closed. + *
+ * + * @todo Might be better to make this an interface. This whole class doesn't really encapsulate a terribly neat + * piece of re-usable functionality. A simple interface defining a close method would suffice. + * + * @todo The convenience method {@link #checkNotClosed} is not that helpfull, what if the caller wants to do something + * other than throw an exception? It doesn't really represent a very usefull re-usable piece of code. Consider + * inlining it and dropping the method. + */ +public abstract class Closeable +{ + /** + * We use an atomic boolean so that we do not have to synchronized access to this flag. Synchronizing access to this + * flag would mean have a synchronized block in every method. + */ + protected final AtomicBoolean _closed = new AtomicBoolean(false); + + /** + * Checks if this is closed, and raises a JMSException if it is. + * + * @throws JMSException If this is closed. + */ + protected void checkNotClosed() throws JMSException + { + if (isClosed()) + { + throw new IllegalStateException("Object " + toString() + " has been closed"); + } + } + + /** + * Checks if this is closed. + * + * @return true if this is closed, false otherwise. + */ + public boolean isClosed() + { + return _closed.get(); + } + + /** + * Closes this object. + * + * @throws JMSException If this cannot be closed for any reason. + */ + public abstract void close() throws JMSException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java new file mode 100644 index 0000000000..b1ec7216bc --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.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.client; + +public class ConnectionTuneParameters +{ + private long _frameMax; + + private int _channelMax; + + private int _heartbeat; + + private long _txnLimit; + + public long getFrameMax() + { + return _frameMax; + } + + public void setFrameMax(long frameMax) + { + _frameMax = frameMax; + } + + public int getChannelMax() + { + return _channelMax; + } + + public void setChannelMax(int channelMax) + { + _channelMax = channelMax; + } + + public int getHeartbeat() + { + return _heartbeat; + } + + public void setHeartbeat(int hearbeat) + { + _heartbeat = hearbeat; + } + + public long getTxnLimit() + { + return _txnLimit; + } + + public void setTxnLimit(long txnLimit) + { + _txnLimit = txnLimit; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java new file mode 100644 index 0000000000..7cc548915c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java @@ -0,0 +1,66 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.qpid.framing.AMQShortString; + +public enum CustomJMSXProperty +{ + JMS_AMQP_NULL, + JMS_QPID_DESTTYPE, + JMSXGroupID, + JMSXGroupSeq, + JMSXUserID; + + + private final AMQShortString _nameAsShortString; + + CustomJMSXProperty() + { + _nameAsShortString = new AMQShortString(toString()); + } + + public AMQShortString getShortStringName() + { + return _nameAsShortString; + } + + private static Enumeration _names; + + public static synchronized Enumeration asEnumeration() + { + if(_names == null) + { + CustomJMSXProperty[] properties = values(); + ArrayList nameList = new ArrayList(properties.length); + for(CustomJMSXProperty property : properties) + { + nameList.add(property.toString()); + } + _names = Collections.enumeration(nameList); + } + return _names; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java new file mode 100644 index 0000000000..81a55006ed --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.client; + +import java.util.Queue; + +public abstract class DispatcherCallback +{ + BasicMessageConsumer _consumer; + + public DispatcherCallback(BasicMessageConsumer mc) + { + _consumer = mc; + } + + abstract public void whilePaused(Queue reprocessQueue); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java new file mode 100644 index 0000000000..0927ca3625 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; + +import javax.jms.JMSException; + +/** + * JMSException does not accept wrapped exceptions in its constructor. Presumably this is because it is a relatively old + * Java exception class, before this was added as a default to Throwable. This exception class accepts wrapped exceptions + * as well as error messages, through its constructor, but is a JMSException. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Accept wrapped exceptions as a JMSException. + *
+ */ +public class JMSAMQException extends JMSException +{ + /** + * Creates a JMSException, wrapping another exception class. + * + * @param message The error message. + * @param cause The underlying exception that caused this one. May be null if none is to be set. + */ + public JMSAMQException(String message, Exception cause) + { + super(message); + + if (cause != null) + { + setLinkedException(cause); + } + } + + /** + * @param s The underlying exception. + * + * @deprecated Use the other constructor and write a helpfull message. This one will be deleted. + */ + public JMSAMQException(AMQException s) + { + super(s.getMessage(), String.valueOf(s.getErrorCode())); + setLinkedException(s); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java new file mode 100644 index 0000000000..903514c35f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java @@ -0,0 +1,31 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.JMSException; + +public class JmsNotImplementedException extends JMSException +{ + public JmsNotImplementedException() + { + super("Not implemented"); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java new file mode 100644 index 0000000000..585d6db3fd --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.client; + +public class MessageConsumerPair +{ + BasicMessageConsumer _consumer; + Object _item; + + public MessageConsumerPair(BasicMessageConsumer consumer, Object item) + { + _consumer = consumer; + _item = item; + } + + public BasicMessageConsumer getConsumer() + { + return _consumer; + } + + public Object getItem() + { + return _item; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java new file mode 100644 index 0000000000..3bb5707417 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.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.client; + +import java.util.Enumeration; + +import javax.jms.ConnectionMetaData; +import javax.jms.JMSException; + +import org.apache.qpid.common.QpidProperties; + +public class QpidConnectionMetaData implements ConnectionMetaData +{ + + + QpidConnectionMetaData(AMQConnection conn) + { + } + + public int getJMSMajorVersion() throws JMSException + { + return 1; + } + + public int getJMSMinorVersion() throws JMSException + { + return 1; + } + + public String getJMSProviderName() throws JMSException + { + return "Apache " + QpidProperties.getProductName(); + } + + public String getJMSVersion() throws JMSException + { + return "1.1"; + } + + public Enumeration getJMSXPropertyNames() throws JMSException + { + return CustomJMSXProperty.asEnumeration(); + } + + public int getProviderMajorVersion() throws JMSException + { + return 0; + } + + public int getProviderMinorVersion() throws JMSException + { + return 8; + } + + public String getProviderVersion() throws JMSException + { + return QpidProperties.getProductName() + " (Client: [" + getClientVersion() + "] ; Broker [" + getBrokerVersion() + "] ; Protocol: [ " + + getProtocolVersion() + "] )"; + } + + private String getProtocolVersion() + { + // TODO - Implement based on connection negotiated protocol + return "0.8"; + } + + public String getBrokerVersion() + { + // TODO - get broker version + return ""; + } + + public String getClientVersion() + { + return QpidProperties.getBuildVersion(); + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java new file mode 100644 index 0000000000..7059588367 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.QueueReceiver; + +/** + * Class that wraps a MessageConsumer for backwards JMS compatibility + * Returned by methods in AMQSession etc + */ +public class QueueReceiverAdaptor implements QueueReceiver { + + protected MessageConsumer _consumer; + protected Queue _queue; + + protected QueueReceiverAdaptor(Queue queue, MessageConsumer consumer) + { + _consumer = consumer; + _queue = queue; + } + + public String getMessageSelector() throws JMSException + { + checkPreConditions(); + return _consumer.getMessageSelector(); + } + + public MessageListener getMessageListener() throws JMSException + { + checkPreConditions(); + return _consumer.getMessageListener(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException + { + checkPreConditions(); + _consumer.setMessageListener(messageListener); + } + + public Message receive() throws JMSException + { + checkPreConditions(); + return _consumer.receive(); + } + + public Message receive(long l) throws JMSException + { + checkPreConditions(); + return _consumer.receive(l); + } + + public Message receiveNoWait() throws JMSException + { + checkPreConditions(); + return _consumer.receiveNoWait(); + } + + public void close() throws JMSException + { + _consumer.close(); + } + + /** + * Return the queue associated with this receiver + * @return + * @throws JMSException + */ + public Queue getQueue() throws JMSException + { + checkPreConditions(); + return _queue; + } + + private void checkPreConditions() throws javax.jms.IllegalStateException { + BasicMessageConsumer msgConsumer = (BasicMessageConsumer)_consumer; + + if (msgConsumer.isClosed() ){ + throw new javax.jms.IllegalStateException("Consumer is closed"); + } + + if(_queue == null){ + throw new UnsupportedOperationException("Queue is null"); + } + + AMQSession session = msgConsumer.getSession(); + + if(session == null || session.isClosed()){ + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java new file mode 100644 index 0000000000..27783bcacf --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -0,0 +1,228 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.client; + +import javax.jms.Destination; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueSender; + +public class QueueSenderAdapter implements QueueSender +{ + + private BasicMessageProducer _delegate; + private Queue _queue; + private boolean closed = false; + + public QueueSenderAdapter(BasicMessageProducer msgProducer, Queue queue) + { + _delegate = msgProducer; + _queue = queue; + } + + public Queue getQueue() throws JMSException + { + checkPreConditions(); + + return _queue; + } + + public void send(Message msg) throws JMSException + { + checkPreConditions(); + _delegate.send(msg); + } + + public void send(Queue queue, Message msg) throws JMSException + { + checkPreConditions(queue); + _delegate.send(queue, msg); + } + + public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions(); + _delegate.send(msg, deliveryMode, priority, timeToLive); + } + + public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions(queue); + _delegate.send(queue, msg, deliveryMode, priority, timeToLive); + } + + public void close() throws JMSException + { + _delegate.close(); + closed = true; + } + + public int getDeliveryMode() throws JMSException + { + checkPreConditions(); + + return _delegate.getDeliveryMode(); + } + + public Destination getDestination() throws JMSException + { + checkPreConditions(); + + return _delegate.getDestination(); + } + + public boolean getDisableMessageID() throws JMSException + { + checkPreConditions(); + + return _delegate.getDisableMessageID(); + } + + public boolean getDisableMessageTimestamp() throws JMSException + { + checkPreConditions(); + + return _delegate.getDisableMessageTimestamp(); + } + + public int getPriority() throws JMSException + { + checkPreConditions(); + + return _delegate.getPriority(); + } + + public long getTimeToLive() throws JMSException + { + checkPreConditions(); + + return _delegate.getTimeToLive(); + } + + public void send(Destination dest, Message msg) throws JMSException + { + checkPreConditions((Queue) dest); + _delegate.send(dest, msg); + } + + public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions(); + _delegate.send(msg, deliveryMode, priority, timeToLive); + } + + public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions((Queue) dest); + _delegate.send(dest, msg, deliveryMode, priority, timeToLive); + } + + public void setDeliveryMode(int deliveryMode) throws JMSException + { + checkPreConditions(); + _delegate.setDeliveryMode(deliveryMode); + } + + public void setDisableMessageID(boolean disableMessageID) throws JMSException + { + checkPreConditions(); + _delegate.setDisableMessageID(disableMessageID); + } + + public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException + { + checkPreConditions(); + _delegate.setDisableMessageTimestamp(disableMessageTimestamp); + } + + public void setPriority(int priority) throws JMSException + { + checkPreConditions(); + _delegate.setPriority(priority); + } + + public void setTimeToLive(long timeToLive) throws JMSException + { + checkPreConditions(); + _delegate.setTimeToLive(timeToLive); + } + + private void checkPreConditions() throws JMSException + { + checkPreConditions(_queue); + } + + private void checkPreConditions(Queue queue) throws JMSException + { + if (closed) + { + throw new javax.jms.IllegalStateException("Publisher is closed"); + } + + AMQSession session = ((BasicMessageProducer) _delegate).getSession(); + + if ((session == null) || session.isClosed()) + { + throw new javax.jms.IllegalStateException("Invalid Session"); + } + + if (queue == null) + { + throw new UnsupportedOperationException("Queue is null."); + } + + if (!(queue instanceof AMQDestination)) + { + throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); + } + + AMQDestination destination = (AMQDestination) queue; + if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) + { + + if (_delegate.getSession().isStrictAMQP()) + { + _delegate._logger.warn("AMQP does not support destination validation before publish, "); + destination.setCheckedForQueueBinding(true); + } + else + { + if (_delegate.isBound(destination)) + { + destination.setCheckedForQueueBinding(true); + } + else + { + throw new InvalidDestinationException("Queue: " + queue + + " is not a valid destination (no bindings on server"); + } + } + } + } + + private boolean checkQueueBeforePublish() + { + return "true".equalsIgnoreCase(System.getProperty("org.apache.qpid.client.verifyQueueBindingBeforePublish", "true")); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java new file mode 100644 index 0000000000..2280cc9870 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.client; + +public class SSLConfiguration { + + private String _keystorePath; + + private String _keystorePassword; + + private String _certType = "SunX509"; + + public void setKeystorePath(String path) + { + _keystorePath = path; + } + + public String getKeystorePath() + { + return _keystorePath; + } + + public void setKeystorePassword(String password) + { + _keystorePassword = password; + } + + public String getKeystorePassword() + { + return _keystorePassword; + } + + public void setCertType(String type) + { + _certType = type; + } + + public String getCertType() + { + return _certType; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java new file mode 100644 index 0000000000..7f8e80c73a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.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.client; + +import javax.jms.Destination; +import javax.jms.JMSException; + +/** + * Provides support for covenience interface implemented by both AMQTemporaryTopic and AMQTemporaryQueue + * so that operations related to their "temporary-ness" can be abstracted out. + */ +interface TemporaryDestination extends Destination +{ + + public void delete() throws JMSException; + public AMQSession getSession(); + public boolean isDeleted(); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java new file mode 100644 index 0000000000..81b9940ed5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java @@ -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. + * + * + */ + +package org.apache.qpid.client; + +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Topic; +import javax.jms.TopicPublisher; + +public class TopicPublisherAdapter implements TopicPublisher +{ + + private BasicMessageProducer _delegate; + private Topic _topic; + + public TopicPublisherAdapter(BasicMessageProducer msgProducer, Topic topic) + { + _delegate = msgProducer; + _topic = topic; + } + + public Topic getTopic() throws JMSException + { + checkPreConditions(); + return _topic; + } + + public void publish(Message msg) throws JMSException + { + checkPreConditions(); + checkTopic(_topic); + _delegate.send(msg); + } + + public void publish(Topic topic, Message msg) throws JMSException + { + checkPreConditions(); + checkTopic(topic); + _delegate.send(topic, msg); + } + + public void publish(Message msg, int deliveryMode, int priority, long timeToLive) + throws JMSException + { + checkPreConditions(); + checkTopic(_topic); + _delegate.send(msg, deliveryMode, priority, timeToLive); + } + + public int getDeliveryMode() throws JMSException { + checkPreConditions(); + return _delegate.getDeliveryMode(); + } + + public void publish(Topic topic, Message msg, int deliveryMode, int priority, long timeToLive) + throws JMSException + { + checkPreConditions(); + checkTopic(topic); + _delegate.send(topic, msg, deliveryMode, priority, timeToLive); + } + + public void close() throws JMSException + { + _delegate.close(); + } + + public boolean getDisableMessageID() throws JMSException { + checkPreConditions(); + return _delegate.getDisableMessageID(); + } + + public boolean getDisableMessageTimestamp() throws JMSException { + checkPreConditions(); + return _delegate.getDisableMessageTimestamp(); + } + + public Destination getDestination() throws JMSException + { + checkPreConditions(); + return _delegate.getDestination(); + } + + public int getPriority() throws JMSException { + checkPreConditions(); + return _delegate.getPriority(); + } + + public long getTimeToLive() throws JMSException { + checkPreConditions(); + return _delegate.getTimeToLive(); + } + + public void send(Message msg) throws JMSException + { + checkPreConditions(); + checkTopic(_topic); + _delegate.send(msg); + } + + public void send(Destination dest, Message msg) throws JMSException + { + checkPreConditions(); + checkTopic(_topic); + _delegate.send(dest, msg); + } + + public void send(Message msg, int deliveryMode, int priority, long timeToLive) + throws JMSException + { + checkPreConditions(); + checkTopic(_topic); + _delegate.send(msg, deliveryMode, priority, timeToLive); + } + + public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException + { + checkPreConditions(); + checkTopic(dest); + _delegate.send(dest, msg, deliveryMode, priority, timeToLive); + } + + public void setDeliveryMode(int deliveryMode) throws JMSException + { + checkPreConditions(); + _delegate.setDeliveryMode(deliveryMode); + } + + public void setDisableMessageID(boolean disableMessageID) throws JMSException + { + checkPreConditions(); + _delegate.setDisableMessageID(disableMessageID); + } + + public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException + { + checkPreConditions(); + _delegate.setDisableMessageTimestamp(disableMessageTimestamp); + } + + public void setPriority(int priority) throws JMSException + { + checkPreConditions(); + _delegate.setPriority(priority); + } + + public void setTimeToLive(long timeToLive) throws JMSException + { + checkPreConditions(); + _delegate.setTimeToLive(timeToLive); + } + + private void checkPreConditions() throws IllegalStateException + { + if (_delegate.isClosed()) + { + throw new javax.jms.IllegalStateException("Publisher is _closed"); + } + + AMQSession session = _delegate.getSession(); + if (session == null || session.isClosed()) + { + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + + private void checkTopic(Destination topic) throws InvalidDestinationException + { + if (topic == null) + { + throw new UnsupportedOperationException("Topic is null"); + } + if (!(topic instanceof Topic)) + { + throw new InvalidDestinationException("Destination " + topic + " is not a topic"); + } + if(!(topic instanceof AMQDestination)) + { + throw new InvalidDestinationException("Destination " + topic + " is not a Qpid topic"); + } + + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java new file mode 100644 index 0000000000..9bdef22f96 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java @@ -0,0 +1,132 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** + * Wraps a MessageConsumer to fulfill the extended TopicSubscriber contract + * + */ +class TopicSubscriberAdaptor implements TopicSubscriber +{ + private final Topic _topic; + private final C _consumer; + private final boolean _noLocal; + + TopicSubscriberAdaptor(Topic topic, C consumer, boolean noLocal) + { + _topic = topic; + _consumer = consumer; + _noLocal = noLocal; + } + + TopicSubscriberAdaptor(Topic topic, C consumer) + { + this(topic, consumer, consumer.isNoLocal()); + } + + public Topic getTopic() throws JMSException + { + checkPreConditions(); + return _topic; + } + + public boolean getNoLocal() throws JMSException + { + checkPreConditions(); + return _noLocal; + } + + public String getMessageSelector() throws JMSException + { + checkPreConditions(); + return _consumer.getMessageSelector(); + } + + public MessageListener getMessageListener() throws JMSException + { + checkPreConditions(); + return _consumer.getMessageListener(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException + { + checkPreConditions(); + _consumer.setMessageListener(messageListener); + } + + public Message receive() throws JMSException + { + checkPreConditions(); + return _consumer.receive(); + } + + public Message receive(long l) throws JMSException + { + return _consumer.receive(l); + } + + public Message receiveNoWait() throws JMSException + { + checkPreConditions(); + return _consumer.receiveNoWait(); + } + + public void close() throws JMSException + { + _consumer.close(); + } + + private void checkPreConditions() throws javax.jms.IllegalStateException{ + C msgConsumer = _consumer; + + if (msgConsumer.isClosed() ){ + throw new javax.jms.IllegalStateException("Consumer is closed"); + } + + if(_topic == null){ + throw new UnsupportedOperationException("Topic is null"); + } + + AMQSession session = msgConsumer.getSession(); + + if(session == null || session.isClosed()){ + throw new javax.jms.IllegalStateException("Invalid Session"); + } + } + + C getMessageConsumer() + { + return _consumer; + } + + public void addBindingKey(Topic topic, String bindingKey) throws AMQException + { + _consumer.addBindingKey((AMQDestination) topic, bindingKey); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java new file mode 100644 index 0000000000..20fa68605a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAConnectionImpl.java @@ -0,0 +1,78 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client; + +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.AMQException; + +import javax.jms.*; + +/** + * This class implements the javax.njms.XAConnection interface + */ +public class XAConnectionImpl extends AMQConnection implements XAConnection, XAQueueConnection, XATopicConnection +{ + //-- constructor + /** + * Create a XAConnection from a connectionURL + */ + public XAConnectionImpl(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException + { + super(connectionURL, sslConfig); + } + + //-- interface XAConnection + /** + * Creates an XASession. + * + * @return A newly created XASession. + * @throws JMSException If the XAConnectiono fails to create an XASession due to + * some internal error. + */ + public synchronized XASession createXASession() throws JMSException + { + checkNotClosed(); + return _delegate.createXASession(_maxPrefetch, _maxPrefetch / 2); + } + + //-- Interface XAQueueConnection + /** + * Creates an XAQueueSession. + * + * @return A newly created XASession. + * @throws JMSException If the XAQueueConnectionImpl fails to create an XASession due to + * some internal error. + */ + public XAQueueSession createXAQueueSession() throws JMSException + { + return (XAQueueSession) createXASession(); + } + + //-- Interface XATopicConnection + /** + * Creates an XAQueueSession. + * + * @return A newly created XASession. + * @throws JMSException If the XAQueueConnectionImpl fails to create an XASession due to + * some internal error. + */ + public XATopicSession createXATopicSession() throws JMSException + { + return (XATopicSession) createXASession(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java new file mode 100644 index 0000000000..35adda9348 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XAResourceImpl.java @@ -0,0 +1,529 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.apache.qpid.QpidException; +import org.apache.qpid.dtx.XidImpl; +import org.apache.qpid.transport.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is an implementation of javax.njms.XAResource. + */ +public class XAResourceImpl implements XAResource +{ + /** + * this XAResourceImpl's logger + */ + private static final Logger _logger = LoggerFactory.getLogger(XAResourceImpl.class); + + /** + * Reference to the associated XASession + */ + private XASessionImpl _xaSession = null; + + /** + * The XID of this resource + */ + private Xid _xid; + + //--- constructor + + /** + * Create an XAResource associated with a XASession + * + * @param xaSession The session XAresource + */ + protected XAResourceImpl(XASessionImpl xaSession) + { + _xaSession = xaSession; + } + + //--- The XAResource + /** + * Commits the global transaction specified by xid. + * + * @param xid A global transaction identifier + * @param b If true, use a one-phase commit protocol to commit the work done on behalf of xid. + * @throws XAException An error has occurred. An error has occurred. Possible XAExceptions are XA_HEURHAZ, + * XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO. + */ + public void commit(Xid xid, boolean b) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("commit tx branch with xid: ", xid); + } + Future future = + _xaSession.getQpidSession().dtxCommit(convertXid(xid), b ? Option.ONE_PHASE : Option.NONE); + + // now wait on the future for the result + XaResult result = null; + try + { + result = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + } + checkStatus(result.getStatus()); + } + + /** + * Ends the work performed on behalf of a transaction branch. + * The resource manager disassociates the XA resource from the transaction branch specified + * and lets the transaction complete. + *

    + *
  • If TMSUSPEND is specified in the flags, the transaction branch is temporarily suspended in an incomplete state. + * The transaction context is in a suspended state and must be resumed via the start method with TMRESUME specified. + *
  • If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction as rollback-only + *
  • If TMSUCCESS is specified, the portion of work has completed successfully. + * /ul> + * + * @param xid A global transaction identifier that is the same as the identifier used previously in the start method + * @param flag One of TMSUCCESS, TMFAIL, or TMSUSPEND. + * @throws XAException An error has occurred. An error has occurred. Possible XAException values are XAER_RMERR, + * XAER_RMFAILED, XAER_NOTA, XAER_INVAL, XAER_PROTO, or XA_RB*. + */ + public void end(Xid xid, int flag) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("end tx branch with xid: ", xid); + } + switch (flag) + { + case(XAResource.TMSUCCESS): + break; + case(XAResource.TMFAIL): + break; + case(XAResource.TMSUSPEND): + break; + default: + throw new XAException(XAException.XAER_INVAL); + } + _xaSession.flushAcknowledgments(); + Future future = _xaSession.getQpidSession() + .dtxEnd(convertXid(xid), + flag == XAResource.TMFAIL ? Option.FAIL : Option.NONE, + flag == XAResource.TMSUSPEND ? Option.SUSPEND : Option.NONE); + // now wait on the future for the result + XaResult result = null; + try + { + result = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + } + checkStatus(result.getStatus()); + } + + + /** + * Tells the resource manager to forget about a heuristically completed transaction branch. + * + * @param xid String(xid.getGlobalTransactionId() A global transaction identifier + * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, + * XAER_NOTA, XAER_INVAL, or XAER_PROTO. + */ + public void forget(Xid xid) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("forget tx branch with xid: ", xid); + } + _xaSession.getQpidSession().dtxForget(convertXid(xid)); + try + { + _xaSession.getQpidSession().sync(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + } + } + + + /** + * Obtains the current transaction timeout value set for this XAResource instance. + * If XAResource.setTransactionTimeout was not used prior to invoking this method, + * the return value is the default timeout i.e. 0; + * otherwise, the value used in the previous setTransactionTimeout call is returned. + * + * @return The transaction timeout value in seconds. + * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL. + */ + public int getTransactionTimeout() throws XAException + { + int result = 0; + if (_xid != null) + { + Future future = + _xaSession.getQpidSession().dtxGetTimeout(convertXid(_xid)); + try + { + result = (int) future.get().getTimeout(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + } + } + return result; + } + + /** + * This method is called to determine if the resource manager instance represented + * by the target object is the same as the resouce manager instance represented by + * the parameter xaResource. + * + * @param xaResource An XAResource object whose resource manager instance is to + * be compared with the resource manager instance of the target object + * @return true if it's the same RM instance; otherwise false. + * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL. + */ + public boolean isSameRM(XAResource xaResource) throws XAException + { + // TODO : get the server identity of xaResource and compare it with our own one + return false; + } + + /** + * Prepare for a transaction commit of the transaction specified in Xid. + * + * @param xid A global transaction identifier. + * @return A value indicating the resource manager's vote on the outcome of the transaction. + * The possible values are: XA_RDONLY or XA_OK. + * @throws XAException An error has occurred. Possible exception values are: XAER_RMERR or XAER_NOTA + */ + public int prepare(Xid xid) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("prepare ", xid); + } + Future future = _xaSession.getQpidSession().dtxPrepare(convertXid(xid)); + XaResult result = null; + try + { + result = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + } + DtxXaStatus status = result.getStatus(); + int outcome = XAResource.XA_OK; + switch (status) + { + case XA_OK: + break; + case XA_RDONLY: + outcome = XAResource.XA_RDONLY; + break; + default: + checkStatus(status); + } + return outcome; + } + + /** + * Obtains a list of prepared transaction branches. + *

    + * The transaction manager calls this method during recovery to obtain the list of transaction branches + * that are currently in prepared or heuristically completed states. + * + * @param flag One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. + * TMNOFLAGS must be used when no other flags are set in the parameter. + * @return zero or more XIDs of the transaction branches that are currently in a prepared or heuristically + * completed state. + * @throws XAException An error has occurred. Possible value is XAER_INVAL. + */ + public Xid[] recover(int flag) throws XAException + { + // the flag is ignored + Future future = _xaSession.getQpidSession().dtxRecover(); + RecoverResult res = null; + try + { + res = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr( e.getException().getErrorCode()); + } + Xid[] result = new Xid[res.getInDoubt().size()]; + int i = 0; + for (Object obj : res.getInDoubt()) + { + org.apache.qpid.transport.Xid xid = (org.apache.qpid.transport.Xid) obj; + result[i] = new XidImpl(xid.getBranchId(), (int) xid.getFormat(), xid.getGlobalId()); + i++; + } + return result; + } + + /** + * Informs the resource manager to roll back work done on behalf of a transaction branch + * + * @param xid A global transaction identifier. + * @throws XAException An error has occurred. + */ + public void rollback(Xid xid) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("rollback tx branch with xid: ", xid); + } + + Future future = _xaSession.getQpidSession().dtxRollback(convertXid(xid)); + // now wait on the future for the result + XaResult result = null; + try + { + result = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr( e.getException().getErrorCode()); + } + checkStatus(result.getStatus()); + } + + /** + * Sets the current transaction timeout value for this XAResource instance. + * Once set, this timeout value is effective until setTransactionTimeout is + * invoked again with a different value. + * To reset the timeout value to the default value used by the resource manager, set the value to zero. + * + * @param timeout The transaction timeout value in seconds. + * @return true if transaction timeout value is set successfully; otherwise false. + * @throws XAException An error has occurred. Possible exception values are XAER_RMERR, XAER_RMFAIL, or XAER_INVAL. + */ + public boolean setTransactionTimeout(int timeout) throws XAException + { + boolean result = false; + if (_xid != null) + { + try + { + _xaSession.getQpidSession() + .dtxSetTimeout(XidImpl.convert(_xid), timeout); + } + catch (QpidException e) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Cannot convert Xid into String format ", e); + } + throw new XAException(XAException.XAER_PROTO); + } + result = true; + } + return result; + } + + /** + * Starts work on behalf of a transaction branch specified in xid. + *

      + *
    • If TMJOIN is specified, an exception is thrown as it is not supported + *
    • If TMRESUME is specified, the start applies to resuming a suspended transaction specified in the parameter xid. + *
    • If neither TMJOIN nor TMRESUME is specified and the transaction specified by xid has previously been seen by the + * resource manager, the resource manager throws the XAException exception with XAER_DUPID error code. + *
    + * + * @param xid A global transaction identifier to be associated with the resource + * @param flag One of TMNOFLAGS, TMJOIN, or TMRESUME + * @throws XAException An error has occurred. Possible exceptions + * are XA_RB*, XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO. + */ + public void start(Xid xid, int flag) throws XAException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("start tx branch with xid: ", xid); + } + switch (flag) + { + case(XAResource.TMNOFLAGS): + break; + case(XAResource.TMJOIN): + break; + case(XAResource.TMRESUME): + break; + default: + throw new XAException(XAException.XAER_INVAL); + } + Future future = _xaSession.getQpidSession() + .dtxStart(convertXid(xid), + flag == XAResource.TMJOIN ? Option.JOIN : Option.NONE, + flag == XAResource.TMRESUME ? Option.RESUME : Option.NONE); + // now wait on the future for the result + XaResult result = null; + try + { + result = future.get(); + } + catch (SessionException e) + { + // we need to restore the qpid session that has been closed + _xaSession.createSession(); + convertExecutionErrorToXAErr(e.getException().getErrorCode()); + // TODO: The amqp spec does not allow to make the difference + // between an already known XID and a wrong arguments (join and resume are set) + // TODO: make sure amqp addresses that + } + checkStatus(result.getStatus()); + _xid = xid; + } + + //------------------------------------------------------------------------ + // Private methods + //------------------------------------------------------------------------ + + /** + * Check xa method outcome and, when required, convert the status into the corresponding xa exception + * @param status method status code + * @throws XAException corresponding XA Exception when required + */ + private void checkStatus(DtxXaStatus status) throws XAException + { + switch (status) + { + case XA_OK: + // Do nothing this ok + break; + case XA_RBROLLBACK: + // The tx has been rolled back for an unspecified reason. + throw new XAException(XAException.XA_RBROLLBACK); + case XA_RBTIMEOUT: + // The transaction branch took too long. + throw new XAException(XAException.XA_RBTIMEOUT); + case XA_HEURHAZ: + // The transaction branch may have been heuristically completed. + throw new XAException(XAException.XA_HEURHAZ); + case XA_HEURCOM: + // The transaction branch has been heuristically committed. + throw new XAException(XAException.XA_HEURCOM); + case XA_HEURRB: + // The transaction branch has been heuristically rolled back. + throw new XAException(XAException.XA_HEURRB); + case XA_HEURMIX: + // The transaction branch has been heuristically committed and rolled back. + throw new XAException(XAException.XA_HEURMIX); + case XA_RDONLY: + // The transaction branch was read-only and has been committed. + throw new XAException(XAException.XA_RDONLY); + default: + // this should not happen + if (_logger.isDebugEnabled()) + { + _logger.debug("got unexpected status value: ", status); + } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); + } + } + + /** + * Convert execution error to xa exception. + * @param error the execution error code + * @throws XAException + */ + private void convertExecutionErrorToXAErr(ExecutionErrorCode error) throws XAException + { + switch (error) + { + case NOT_ALLOWED: + // The XID already exists. + throw new XAException(XAException.XAER_DUPID); + case NOT_FOUND: + // The XID is not valid. + throw new XAException(XAException.XAER_NOTA); + case ILLEGAL_STATE: + // Routine was invoked in an inproper context. + throw new XAException(XAException.XAER_PROTO); + case NOT_IMPLEMENTED: + // the command is not implemented + throw new XAException(XAException.XAER_RMERR); + case COMMAND_INVALID: + // Invalid call + throw new XAException(XAException.XAER_INVAL); + default: + // this should not happen + if (_logger.isDebugEnabled()) + { + _logger.debug("Got unexpected error: " + error); + } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); + } + } + + /** + * convert a generic xid into qpid format + * @param xid xid to be converted + * @return the qpid formated xid + * @throws XAException when xid is null or when it cannot be converted. + */ + private org.apache.qpid.transport.Xid convertXid(Xid xid) throws XAException + { + if (xid == null) + { + // Invalid arguments were given. + throw new XAException(XAException.XAER_INVAL); + } + try + { + return XidImpl.convert(xid); + } + catch (QpidException e) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Cannot convert Xid into String format ", e); + } + //A resource manager error has occured in the transaction branch. + throw new XAException(XAException.XAER_RMERR); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java new file mode 100644 index 0000000000..354b67cd35 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/XASessionImpl.java @@ -0,0 +1,159 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client; + +import org.apache.qpid.client.message.MessageFactoryRegistry; + +import javax.jms.*; +import javax.transaction.xa.XAResource; + +/** + * This is an implementation of the javax.njms.XASEssion interface. + */ +public class XASessionImpl extends AMQSession_0_10 implements XASession, XATopicSession, XAQueueSession +{ + /** + * XAResource associated with this XASession + */ + private final XAResourceImpl _xaResource; + + /** + * This XASession Qpid DtxSession + */ + private org.apache.qpid.transport.Session _qpidDtxSession; + + /** + * The standard session + */ + private Session _jmsSession; + + + //-- Constructors + /** + * Create a JMS XASession + */ + public XASessionImpl(org.apache.qpid.transport.Connection qpidConnection, AMQConnection con, int channelId, + int defaultPrefetchHigh, int defaultPrefetchLow) + { + super(qpidConnection, con, channelId, false, // this is not a transacted session + Session.AUTO_ACKNOWLEDGE, // the ack mode is transacted + MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow); + createSession(); + _xaResource = new XAResourceImpl(this); + } + + //-- public methods + + /** + * Create a qpid session. + */ + public void createSession() + { + _qpidDtxSession = _qpidConnection.createSession(0); + _qpidDtxSession.setSessionListener(this); + _qpidDtxSession.dtxSelect(); + } + + + //--- javax.njms.XASEssion API + + /** + * Gets the session associated with this XASession. + * + * @return The session object. + * @throws JMSException if an internal error occurs. + */ + public Session getSession() throws JMSException + { + if (_jmsSession == null) + { + _jmsSession = getAMQConnection().createSession(true, getAcknowledgeMode()); + } + return _jmsSession; + } + + /** + * Returns an XA resource. + * + * @return An XA resource. + */ + public XAResource getXAResource() + { + return _xaResource; + } + + //-- overwritten mehtods + /** + * Throws a {@link TransactionInProgressException}, since it should + * not be called for an XASession object. + * + * @throws TransactionInProgressException always. + */ + public void commit() throws JMSException + { + throw new TransactionInProgressException( + "XASession: A direct invocation of the commit operation is probibited!"); + } + + /** + * Throws a {@link TransactionInProgressException}, since it should + * not be called for an XASession object. + * + * @throws TransactionInProgressException always. + */ + public void rollback() throws JMSException + { + throw new TransactionInProgressException( + "XASession: A direct invocation of the rollback operation is probibited!"); + } + + /** + * Access to the underlying Qpid Session + * + * @return The associated Qpid Session. + */ + protected org.apache.qpid.transport.Session getQpidSession() + { + return _qpidDtxSession; + } + + //--- interface XAQueueSession + /** + * Gets the topic session associated with this XATopicSession. + * + * @return the topic session object + * @throws JMSException If an internal error occurs. + */ + public QueueSession getQueueSession() throws JMSException + { + return (QueueSession) getSession(); + } + + //--- interface XATopicSession + + /** + * Gets the topic session associated with this XATopicSession. + * + * @return the topic session object + * @throws JMSException If an internal error occurs. + */ + public TopicSession getTopicSession() throws JMSException + { + return (TopicSession) getSession(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java new file mode 100644 index 0000000000..49ac89d9b3 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/configuration/ClientProperties.java @@ -0,0 +1,78 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.qpid.client.configuration; + +/** + * This class centralized the Qpid client properties. + */ +public class ClientProperties +{ + + /** + * Currently with Qpid it is not possible to change the client ID. + * If one is not specified upon connection construction, an id is generated automatically. + * Therefore an exception is always thrown unless this property is set to true. + * type: boolean + */ + public static final String IGNORE_SET_CLIENTID_PROP_NAME = "ignore_setclientID"; + + /** + * This property is currently used within the 0.10 code path only + * The maximum number of pre-fetched messages per destination + * This property is used for all the connection unless it is overwritten by the connectionURL + * type: long + */ + public static final String MAX_PREFETCH_PROP_NAME = "max_prefetch"; + public static final String MAX_PREFETCH_DEFAULT = "5000"; + + /** + * When true a sync command is sent after every persistent messages. + * type: boolean + */ + public static final String SYNC_PERSISTENT_PROP_NAME = "sync_persistence"; + + /** + * ========================================================== + * Those properties are used when the io size should be bounded + * ========================================================== + */ + + /** + * When set to true the io layer throttle down producers and consumers + * when written or read messages are accumulating and exceeding a certain size. + * This is especially useful when a the producer rate is greater than the network + * speed. + * type: boolean + */ + public static final String PROTECTIO_PROP_NAME = "protectio"; + + //=== The following properties are only used when the previous one is true. + /** + * Max size of read messages that can be stored within the MINA layer + * type: int + */ + public static final String READ_BUFFER_LIMIT_PROP_NAME = "qpid.read.buffer.limit"; + public static final String READ_BUFFER_LIMIT_DEFAULT = "262144"; + /** + * Max size of written messages that can be stored within the MINA layer + * type: int + */ + public static final String WRITE_BUFFER_LIMIT_PROP_NAME = "qpid.read.buffer.limit"; + public static final String WRITE_BUFFER_LIMIT_DEFAULT = "262144"; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java new file mode 100644 index 0000000000..037b0dc2d1 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.failover; + +/** + * FailoverException is used to indicate that a synchronous request has failed to receive the reply that it is waiting + * for because the fail-over process has been started whilst it was waiting for its reply. Synchronous methods generally + * raise this exception to indicate that they must be re-tried once the fail-over process has completed. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Used to indicate failure of a synchronous request due to fail-over. + *
    + * + * @todo This exception is created and passed as an argument to a method, rather than thrown. The exception is being + * used to represent an event, passed out to other threads. Use of exceptions as arguments rather than as + * exceptions is extremly confusing. Ideally use a condition or set a flag and check it instead. + * This exceptions-as-events pattern seems to be in a similar style to Mina code, which is not pretty, but + * potentially acceptable for that reason. We have the option of extending the mina model to add more events + * to it, that is, anything that is interested in handling failover as an event occurs below the main + * amq event handler, which knows the specific interface of the qpid handlers, which can pass this down as + * an explicit event, without it being an exception. Add failover method to BlockingMethodFrameListener, + * have it set a flag or interrupt the waiting thread, which then creates and raises this exception. + */ +public class FailoverException extends Exception +{ + public FailoverException(String message) + { + super(message); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java new file mode 100644 index 0000000000..927f660932 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java @@ -0,0 +1,256 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.failover; + +import org.apache.mina.common.IoSession; + +import org.apache.qpid.AMQDisconnectedException; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.AMQStateManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CountDownLatch; + +/** + * FailoverHandler is a continuation that performs the failover procedure on a protocol session. As described in the + * class level comment for {@link AMQProtocolHandler}, a protocol connection can span many physical transport + * connections, failing over to a new connection if the transport connection fails. The procedure to establish a new + * connection is expressed as a continuation, in order that it may be run in a seperate thread to the i/o thread that + * detected the failure and is used to handle the communication to establish a new connection. + * + *

    The reason this needs to be a separate thread is because this work cannot be done inside the i/o processor + * thread. The significant task is the connection setup which involves a protocol exchange until a particular state + * is achieved. This procedure waits until the state is achieved which would prevent the i/o thread doing the work + * it needs to do to achieve the new state. + * + *

    The failover procedure does the following: + * + *

      + *
    1. Sets the failing over condition to true.
    2. + *
    3. Creates a {@link FailoverException} and gets the protocol connection handler to propagate this event to all + * interested parties.
    4. + *
    5. Takes the failover mutex on the protocol connection handler.
    6. + *
    7. Abandons the fail over if any of the interested parties vetoes it. The mutex is released and the condition + * reset.
    8. + *
    9. Creates a new {@link AMQStateManager} and re-established the connection through it.
    10. + *
    11. Informs the AMQConnection if the connection cannot be re-established.
    12. + *
    13. Recreates all sessions from the old connection to the new.
    14. + *
    15. Resets the failing over condition and releases the mutex.
    16. + *
    + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Update fail-over state {@link AMQProtocolHandler} + *
    + * + * @todo The failover latch and mutex are used like a lock and condition. If the retrotranlator supports lock/condition + * then could change over to using them. 1.4 support still needed. + * + * @todo If the condition is set to null on a vetoes fail-over and there are already other threads waiting on the + * condition, they will never be released. It might be an idea to reset the condition in a finally block. + * + * @todo Creates a {@link AMQDisconnectedException} and passes it to the AMQConnection. No need to use an + * exception-as-argument here, could just as easily call a specific method for this purpose on AMQConnection. + * + * @todo Creates a {@link FailoverException} and propagates it to the MethodHandlers. No need to use an + * exception-as-argument here, could just as easily call a specific method for this purpose on + * {@link org.apache.qpid.protocol.AMQMethodListener}. + */ +public class FailoverHandler implements Runnable +{ + /** Used for debugging. */ + private static final Logger _logger = LoggerFactory.getLogger(FailoverHandler.class); + + /** Holds the MINA session for the connection that has failed, not the connection that is being failed onto. */ + private final IoSession _session; + + /** Holds the protocol handler for the failed connection, upon which the new connection is to be set up. */ + private AMQProtocolHandler _amqProtocolHandler; + + /** Used to hold the host to fail over to. This is optional and if not set a reconnect to the previous host is tried. */ + private String _host; + + /** Used to hold the port to fail over to. */ + private int _port; + + /** + * Creates a failover handler on a protocol session, for a particular MINA session (network connection). + * + * @param amqProtocolHandler The protocol handler that spans the failover. + * @param session The MINA session, for the failing connection. + */ + public FailoverHandler(AMQProtocolHandler amqProtocolHandler, IoSession session) + { + _amqProtocolHandler = amqProtocolHandler; + _session = session; + } + + /** + * Performs the failover procedure. See the class level comment, {@link FailoverHandler}, for a description of the + * failover procedure. + */ + public void run() + { + if (Thread.currentThread().isDaemon()) + { + throw new IllegalStateException("FailoverHandler must run on a non-daemon thread."); + } + + // Create a latch, upon which tasks that must not run in parallel with a failover can wait for completion of + // the fail over. + _amqProtocolHandler.setFailoverLatch(new CountDownLatch(1)); + + // We wake up listeners. If they can handle failover, they will extend the + // FailoverRetrySupport class and will in turn block on the latch until failover + // has completed before retrying the operation. + _amqProtocolHandler.notifyFailoverStarting(); + + // Since failover impacts several structures we protect them all with a single mutex. These structures + // are also in child objects of the connection. This allows us to manipulate them without affecting + // client code which runs in a separate thread. + synchronized (_amqProtocolHandler.getConnection().getFailoverMutex()) + { + //Clear the exception now that we have the failover mutex there can be no one else waiting for a frame so + // we can clear the exception. + _amqProtocolHandler.failoverInProgress(); + + // We switch in a new state manager temporarily so that the interaction to get to the "connection open" + // state works, without us having to terminate any existing "state waiters". We could theoretically + // have a state waiter waiting until the connection is closed for some reason. Or in future we may have + // a slightly more complex state model therefore I felt it was worthwhile doing this. + AMQStateManager existingStateManager = _amqProtocolHandler.getStateManager(); + + _amqProtocolHandler.setStateManager(new AMQStateManager()); + + + if (!_amqProtocolHandler.getConnection().firePreFailover(_host != null)) + { + _logger.info("Failover process veto-ed by client"); + + //Restore Existing State Manager + _amqProtocolHandler.setStateManager(existingStateManager); + + //todo: ritchiem these exceptions are useless... Would be better to attempt to propogate exception that + // prompted the failover event. + if (_host != null) + { + _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException("Redirect was vetoed by client", null)); + } + else + { + _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException("Failover was vetoed by client", null)); + } + + _amqProtocolHandler.getFailoverLatch().countDown(); + _amqProtocolHandler.setFailoverLatch(null); + + return; + } + + _logger.info("Starting failover process"); + + boolean failoverSucceeded; + // when host is non null we have a specified failover host otherwise we all the client to cycle through + // all specified hosts + + // if _host has value then we are performing a redirect. + if (_host != null) + { + failoverSucceeded = _amqProtocolHandler.getConnection().attemptReconnection(_host, _port); + } + else + { + failoverSucceeded = _amqProtocolHandler.getConnection().attemptReconnection(); + } + + if (!failoverSucceeded) + { + //Restore Existing State Manager + _amqProtocolHandler.setStateManager(existingStateManager); + + _amqProtocolHandler.getConnection().exceptionReceived( + new AMQDisconnectedException("Server closed connection and no failover " + + "was successful", null)); + } + else + { + // Set the new Protocol Session in the StateManager. + existingStateManager.setProtocolSession(_amqProtocolHandler.getProtocolSession()); + + //Restore Existing State Manager + _amqProtocolHandler.setStateManager(existingStateManager); + try + { + if (_amqProtocolHandler.getConnection().firePreResubscribe()) + { + _logger.info("Resubscribing on new connection"); + _amqProtocolHandler.getConnection().resubscribeSessions(); + } + else + { + _logger.info("Client vetoed automatic resubscription"); + } + + _amqProtocolHandler.getConnection().fireFailoverComplete(); + _amqProtocolHandler.setFailoverState(FailoverState.NOT_STARTED); + _logger.info("Connection failover completed successfully"); + } + catch (Exception e) + { + _logger.info("Failover process failed - exception being propagated by protocol handler"); + _amqProtocolHandler.setFailoverState(FailoverState.FAILED); + /*try + {*/ + _amqProtocolHandler.exceptionCaught(_session, e); + /*} + catch (Exception ex) + { + _logger.error("Error notifying protocol session of error: " + ex, ex); + }*/ + } + } + } + + _amqProtocolHandler.getFailoverLatch().countDown(); + } + + /** + * Sets the host name to fail over to. This is optional and if not set a reconnect to the previous host is tried. + * + * @param host The host name to fail over to. + */ + public void setHost(String host) + { + _host = host; + } + + /** + * Sets the port to fail over to. + * + * @param port The port to fail over to. + */ + public void setPort(int port) + { + _port = port; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java new file mode 100644 index 0000000000..51cc94965a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.client.failover; + +import org.apache.qpid.client.AMQConnection; + +/** + * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support + * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this + * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be + * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception, + * for example, because the caller already holds a lock preventing that condition from arising. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Perform a fail-over protected operation raising providing no handling of fail-over conditions. + *
    + */ +public class FailoverNoopSupport implements FailoverSupport +{ + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverNoopSupport(FailoverProtectedOperation operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delegates to another continuation which is to be provided with fail-over handling. + * + * @return The return value from the delegated to continuation. + * @throws E Any exception that the delegated to continuation may raise. + */ + public T execute() throws E + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + throw new IllegalStateException("Fail-over interupted no-op failover support. " + + "No-op support should only be used where the caller is certain fail-over cannot occur.", e); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java new file mode 100644 index 0000000000..e9c5f24791 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.client.failover; + +/** + * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because + * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers + * for failover protected operations, in order to provide different handling schemes when failovers occurr. + * + *

    The type of checked exception that the operation may perform has been generified, in order that fail over + * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not + * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes + * will mask the exception. + * + *

    + *
    CRC Card
    Responsibilities + *
    Perform an operation that may be interrupted by fail-over. + *
    + */ +public interface FailoverProtectedOperation +{ + /** + * Performs the continuations work. + * + * @return Provdes scope for the continuation to return an arbitrary value. + * + * @throws FailoverException If the operation is interrupted by a fail-over notification. + */ + public abstract T execute() throws E, FailoverException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java new file mode 100644 index 0000000000..e9e52cc97c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.failover; + +import org.apache.qpid.client.AMQConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified + * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due + * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request, + * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This + * automatic retrying is continued until the continuation succeeds, or throws an exception (different to + * FailoverException, which is used to signal the failure of the original condition). + * + *

    The blocking condition used is that the connection is not currently failing over, and the mutex used is the + * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods. + * These are used like a lock and condition variable. + * + *

    The wrapped operation may throw a FailoverException, this is an exception that can be raised by a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a + * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are + * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous + * methods that should not be attempted when a fail-over is in progress. + * + *

    Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want + * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation + * until it succeeds. Synchronous methods are usually coordinated with a + * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants + * to start and throws a FailoverException in response to this. + * + *

    Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be + * started during fail-over, but be delayed until any current fail-over has completed. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Provide a continuation synchronized on a fail-over lock and condition. + *
    Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception. + *
    + * + * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see + * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary + * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation. + * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type + * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that. + * + * @todo InterruptedException not handled well. + */ +public class FailoverRetrySupport implements FailoverSupport +{ + /** Used for debugging. */ + private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class); + + /** The protected operation that is to be retried in the event of fail-over. */ + FailoverProtectedOperation operation; + + /** The connection on which the fail-over protected operation is to be performed. */ + AMQConnection connection; + + /** + * Creates an automatic retrying fail-over handler for the specified operation. + * + * @param operation The fail-over protected operation to wrap in this handler. + */ + public FailoverRetrySupport(FailoverProtectedOperation operation, AMQConnection con) + { + this.operation = operation; + this.connection = con; + } + + /** + * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats + * until the operation throws AMQException or succeeds without being interrupted by fail-over. + * + * @return The result of executing the continuation. + * + * @throws E Any underlying exception is allowed to fall through. + */ + public T execute() throws E + { + return connection.executeRetrySupport(operation); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java new file mode 100644 index 0000000000..807a5f7d13 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.failover; + +/** + * Defines the possible states of the failover process; not started, in progress, failed. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represent a one of the states of the fail-over process. + *
    + */ +public final class FailoverState +{ + /** The string description on this state. */ + private final String _state; + + /** Failover has not yet been attempted on this connection. */ + public static final FailoverState NOT_STARTED = new FailoverState("NOT STARTED"); + + /** Failover has been requested on this connection but has not completed. */ + public static final FailoverState IN_PROGRESS = new FailoverState("IN PROGRESS"); + + /** Failover has been attempted and failed. */ + public static final FailoverState FAILED = new FailoverState("FAILED"); + + /** + * Creates a type safe enumeration of a fail-over state. + * + * @param state The fail-over state description string. + */ + private FailoverState(String state) + { + _state = state; + } + + /** + * Prints this state, mainly for debugging purposes. + * + * @return The string description of this state. + */ + public String toString() + { + return "FailoverState: " + _state; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java new file mode 100644 index 0000000000..ef2e7e1d65 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.client.failover; + +/** + * FailoverSupport defines an interface for different types of fail-over handlers, that provide different types of + * behaviour for handling fail-overs during operations that can be interrupted by the fail-over process. For example, + * the support could automatically retry once the fail-over process completes, could prevent an operation from being + * started whilst fail-over is running, or could quietly abandon the operation or raise an exception, and so on. + * + *

    + *
    CRC Card
    Responsibilities + *
    Perform a fail-over protected operation with handling for fail-over conditions. + *
    + * + * @todo Continuation, extend some sort of re-usable Continuation interface, which might look very like this one. + */ +public interface FailoverSupport +{ + /** + * Delegates to another continuation which is to be provided with fail-over handling. + * + * @return The return value from the delegated to continuation. + * + * @throws E Any exception that the delegated to continuation may raise. + */ + public T execute() throws E; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java new file mode 100644 index 0000000000..af47673a43 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/AccessRequestOkMethodHandler.java @@ -0,0 +1,50 @@ +package org.apache.qpid.client.handler; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.framing.*; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; + +public class AccessRequestOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(AccessRequestOkMethodHandler.class); + + private static AccessRequestOkMethodHandler _handler = new AccessRequestOkMethodHandler(); + + public static AccessRequestOkMethodHandler getInstance() + { + return _handler; + } + + public void methodReceived(AMQProtocolSession session, AccessRequestOkBody method, int channelId) + throws AMQException + { + _logger.debug("AccessRequestOk method received"); + session.setTicket(method.getTicket(), channelId); + + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java new file mode 100644 index 0000000000..5cb9412d51 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicCancelOkBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicCancelOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicCancelOkMethodHandler.class); + + private static final BasicCancelOkMethodHandler _instance = new BasicCancelOkMethodHandler(); + + public static BasicCancelOkMethodHandler getInstance() + { + return _instance; + } + + private BasicCancelOkMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, BasicCancelOkBody body, int channelId) + throws AMQException + { + if (_logger.isInfoEnabled()) + { + _logger.info("New BasicCancelOk method received for consumer:" + body.getConsumerTag()); + } + + session.confirmConsumerCancelled(channelId, body.getConsumerTag()); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java new file mode 100644 index 0000000000..6237234c4d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.UnprocessedMessage_0_8; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicDeliverBody; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicDeliverMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicDeliverMethodHandler.class); + + private static final BasicDeliverMethodHandler _instance = new BasicDeliverMethodHandler(); + + public static BasicDeliverMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQProtocolSession session, BasicDeliverBody body, int channelId) + throws AMQException + { + final UnprocessedMessage_0_8 msg = new UnprocessedMessage_0_8( + body.getDeliveryTag(), + body.getConsumerTag().toIntValue(), + body.getExchange(), + body.getRoutingKey(), + body.getRedelivered()); + _logger.debug("New JmsDeliver method received:" + session); + session.unprocessedMessageReceived(channelId, msg); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java new file mode 100644 index 0000000000..3bbc9209c5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.ReturnMessage; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicReturnBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicReturnMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicReturnMethodHandler.class); + + private static final BasicReturnMethodHandler _instance = new BasicReturnMethodHandler(); + + public static BasicReturnMethodHandler getInstance() + { + return _instance; + } + + + public void methodReceived(AMQProtocolSession session, BasicReturnBody body, int channelId) + throws AMQException + { + _logger.debug("New JmsBounce method received"); + final ReturnMessage msg = new ReturnMessage( + body.getExchange(), + body.getRoutingKey(), + body.getReplyText(), + body.getReplyCode() + ); + + session.unprocessedMessageReceived(channelId, msg); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java new file mode 100644 index 0000000000..2b6745ebe4 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java @@ -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. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQChannelClosedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.protocol.AMQConstant; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelCloseMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandler.class); + + private static ChannelCloseMethodHandler _handler = new ChannelCloseMethodHandler(); + + public static ChannelCloseMethodHandler getInstance() + { + return _handler; + } + + public void methodReceived(AMQProtocolSession session, ChannelCloseBody method, int channelId) + throws AMQException + { + _logger.debug("ChannelClose method received"); + + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); + } + + + + ChannelCloseOkBody body = session.getMethodRegistry().createChannelCloseOkBody(); + AMQFrame frame = body.generateFrame(channelId); + session.writeFrame(frame); + + if (errorCode != AMQConstant.REPLY_SUCCESS) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); + } + + if (errorCode == AMQConstant.NO_CONSUMERS) + { + throw new AMQNoConsumersException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason), null); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); + } + else + { + throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); + } + + } + // fixme why is this only done when the close is expected... + // should the above forced closes not also cause a close? + // ---------- + // Closing the session only when it is expected allows the errors to be processed + // Calling this here will prevent failover. So we should do this for all exceptions + // that should never cause failover. Such as authentication errors. + + session.channelClosed(channelId, errorCode, String.valueOf(reason)); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java new file mode 100644 index 0000000000..9a9a0b4e63 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelCloseOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseOkMethodHandler.class); + + private static final ChannelCloseOkMethodHandler _instance = new ChannelCloseOkMethodHandler(); + + public static ChannelCloseOkMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQProtocolSession session, ChannelCloseOkBody method, int channelId) + throws AMQException + { + _logger.info("Received channel-close-ok for channel-id " + channelId); + + // todo this should do the local closure + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java new file mode 100644 index 0000000000..2153b9cc8c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowMethodHandler.java @@ -0,0 +1,51 @@ +package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.ChannelFlowBody; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.AMQException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ + +public class ChannelFlowMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowMethodHandler.class); + private static final ChannelFlowMethodHandler _instance = new ChannelFlowMethodHandler(); + + public static ChannelFlowMethodHandler getInstance() + { + return _instance; + } + + private ChannelFlowMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ChannelFlowBody body, int channelId) + throws AMQException + { + session.setFlowControl(channelId, body.getActive()); + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java new file mode 100644 index 0000000000..6f66a972d5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ChannelFlowOkBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelFlowOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowOkMethodHandler.class); + private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler(); + + public static ChannelFlowOkMethodHandler getInstance() + { + return _instance; + } + + private ChannelFlowOkMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ChannelFlowOkBody body, int channelId) + throws AMQException + { + + _logger.debug("Received Channel.Flow-Ok message, active = " + body.getActive()); + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java new file mode 100644 index 0000000000..9c791730ca --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl.java @@ -0,0 +1,529 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import java.util.Map; +import java.util.HashMap; + +import org.apache.qpid.framing.*; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientMethodDispatcherImpl implements MethodDispatcher +{ + + private static final BasicCancelOkMethodHandler _basicCancelOkMethodHandler = BasicCancelOkMethodHandler.getInstance(); + private static final BasicDeliverMethodHandler _basicDeliverMethodHandler = BasicDeliverMethodHandler.getInstance(); + private static final BasicReturnMethodHandler _basicReturnMethodHandler = BasicReturnMethodHandler.getInstance(); + private static final ChannelCloseMethodHandler _channelCloseMethodHandler = ChannelCloseMethodHandler.getInstance(); + private static final ChannelFlowOkMethodHandler _channelFlowOkMethodHandler = ChannelFlowOkMethodHandler.getInstance(); + private static final ConnectionCloseMethodHandler _connectionCloseMethodHandler = ConnectionCloseMethodHandler.getInstance(); + private static final ConnectionOpenOkMethodHandler _connectionOpenOkMethodHandler = ConnectionOpenOkMethodHandler.getInstance(); + private static final ConnectionRedirectMethodHandler _connectionRedirectMethodHandler = ConnectionRedirectMethodHandler.getInstance(); + private static final ConnectionSecureMethodHandler _connectionSecureMethodHandler = ConnectionSecureMethodHandler.getInstance(); + private static final ConnectionStartMethodHandler _connectionStartMethodHandler = ConnectionStartMethodHandler.getInstance(); + private static final ConnectionTuneMethodHandler _connectionTuneMethodHandler = ConnectionTuneMethodHandler.getInstance(); + private static final ExchangeBoundOkMethodHandler _exchangeBoundOkMethodHandler = ExchangeBoundOkMethodHandler.getInstance(); + private static final QueueDeleteOkMethodHandler _queueDeleteOkMethodHandler = QueueDeleteOkMethodHandler.getInstance(); + + private static final Logger _logger = LoggerFactory.getLogger(ClientMethodDispatcherImpl.class); + + private static interface DispatcherFactory + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQProtocolSession session); + } + + private static final Map _dispatcherFactories = + new HashMap(); + + static + { + _dispatcherFactories.put(ProtocolVersion.v8_0, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQProtocolSession session) + { + return new ClientMethodDispatcherImpl_8_0(session); + } + }); + + _dispatcherFactories.put(ProtocolVersion.v0_9, + new DispatcherFactory() + { + public ClientMethodDispatcherImpl createMethodDispatcher(AMQProtocolSession session) + { + return new ClientMethodDispatcherImpl_0_9(session); + } + }); + + } + + public static ClientMethodDispatcherImpl newMethodDispatcher(ProtocolVersion version, AMQProtocolSession session) + { + if (_logger.isInfoEnabled()) + { + _logger.info("New Method Dispatcher:" + session); + } + + DispatcherFactory factory = _dispatcherFactories.get(version); + return factory.createMethodDispatcher(session); + } + + AMQProtocolSession _session; + + public ClientMethodDispatcherImpl(AMQProtocolSession session) + { + _session = session; + } + + public AMQStateManager getStateManager() + { + return _session.getStateManager(); + } + + public boolean dispatchAccessRequestOk(AccessRequestOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicCancelOk(BasicCancelOkBody body, int channelId) throws AMQException + { + _basicCancelOkMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchBasicConsumeOk(BasicConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicDeliver(BasicDeliverBody body, int channelId) throws AMQException + { + _basicDeliverMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchBasicGetEmpty(BasicGetEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicGetOk(BasicGetOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicQosOk(BasicQosOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicReturn(BasicReturnBody body, int channelId) throws AMQException + { + _basicReturnMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchChannelClose(ChannelCloseBody body, int channelId) throws AMQException + { + _channelCloseMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchChannelCloseOk(ChannelCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlow(ChannelFlowBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelFlowOk(ChannelFlowOkBody body, int channelId) throws AMQException + { + _channelFlowOkMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchChannelOpenOk(ChannelOpenOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionClose(ConnectionCloseBody body, int channelId) throws AMQException + { + _connectionCloseMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchConnectionCloseOk(ConnectionCloseOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchConnectionOpenOk(ConnectionOpenOkBody body, int channelId) throws AMQException + { + _connectionOpenOkMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchConnectionRedirect(ConnectionRedirectBody body, int channelId) throws AMQException + { + _connectionRedirectMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchConnectionSecure(ConnectionSecureBody body, int channelId) throws AMQException + { + _connectionSecureMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchConnectionStart(ConnectionStartBody body, int channelId) throws AMQException + { + _connectionStartMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchConnectionTune(ConnectionTuneBody body, int channelId) throws AMQException + { + _connectionTuneMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchQueueDeleteOk(QueueDeleteOkBody body, int channelId) throws AMQException + { + _queueDeleteOkMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchQueuePurgeOk(QueuePurgeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamCancelOk(StreamCancelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamConsumeOk(StreamConsumeOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchAccessRequest(AccessRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicAck(BasicAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicCancel(BasicCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicConsume(BasicConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicGet(BasicGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicPublish(BasicPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicQos(BasicQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicRecover(BasicRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchBasicReject(BasicRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOpen(ChannelOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionOpen(ConnectionOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionSecureOk(ConnectionSecureOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionStartOk(ConnectionStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchConnectionTuneOk(ConnectionTuneOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelect(DtxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStart(DtxStartBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBound(ExchangeBoundBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDeclare(ExchangeDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeDelete(ExchangeDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileAck(FileAckBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileCancel(FileCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsume(FileConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFilePublish(FilePublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQos(FileQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReject(FileRejectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBind(QueueBindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDeclare(QueueDeclareBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueDelete(QueueDeleteBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueuePurge(QueuePurgeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamCancel(StreamCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamConsume(StreamConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamPublish(StreamPublishBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQos(StreamQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTunnelRequest(TunnelRequestBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommit(TxCommitBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxRollback(TxRollbackBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxSelect(TxSelectBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxSelectOk(DtxSelectOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchDtxStartOk(DtxStartOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchExchangeBoundOk(ExchangeBoundOkBody body, int channelId) throws AMQException + { + _exchangeBoundOkMethodHandler.methodReceived(_session, body, channelId); + return true; + } + + public boolean dispatchExchangeDeclareOk(ExchangeDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchExchangeDeleteOk(ExchangeDeleteOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchFileCancelOk(FileCancelOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileConsumeOk(FileConsumeOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileDeliver(FileDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpen(FileOpenBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileOpenOk(FileOpenOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileQosOk(FileQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileReturn(FileReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchFileStage(FileStageBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueBindOk(QueueBindOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueDeclareOk(QueueDeclareOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchStreamDeliver(StreamDeliverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamQosOk(StreamQosOkBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchStreamReturn(StreamReturnBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchTxCommitOk(TxCommitOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxRollbackOk(TxRollbackOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTxSelectOk(TxSelectOkBody body, int channelId) throws AMQException + { + return false; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java new file mode 100644 index 0000000000..d3e9fba8ed --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_0_9.java @@ -0,0 +1,153 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.MethodDispatcher_0_9; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQMethodNotImplementedException; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +public class ClientMethodDispatcherImpl_0_9 extends ClientMethodDispatcherImpl implements MethodDispatcher_0_9 +{ + public ClientMethodDispatcherImpl_0_9(AMQProtocolSession session) + { + super(session); + } + + public boolean dispatchBasicRecoverSyncOk(BasicRecoverSyncOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchBasicRecoverSync(BasicRecoverSyncBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchChannelOk(ChannelOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPing(ChannelPingBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelPong(ChannelPongBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelResume(ChannelResumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageAppend(MessageAppendBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageCancel(MessageCancelBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageCheckpoint(MessageCheckpointBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageClose(MessageCloseBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageConsume(MessageConsumeBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageEmpty(MessageEmptyBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageGet(MessageGetBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageOffset(MessageOffsetBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOk(MessageOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageOpen(MessageOpenBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageQos(MessageQosBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageRecover(MessageRecoverBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchMessageReject(MessageRejectBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageResume(MessageResumeBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchMessageTransfer(MessageTransferBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchQueueUnbind(QueueUnbindBody body, int channelId) throws AMQException + { + throw new AMQMethodNotImplementedException(body); + } + + public boolean dispatchQueueUnbindOk(QueueUnbindOkBody body, int channelId) throws AMQException + { + return false; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java new file mode 100644 index 0000000000..19f758817d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ClientMethodDispatcherImpl_8_0.java @@ -0,0 +1,85 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + package org.apache.qpid.client.handler; + +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_8_0.MethodDispatcher_8_0; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +public class ClientMethodDispatcherImpl_8_0 extends ClientMethodDispatcherImpl implements MethodDispatcher_8_0 +{ + public ClientMethodDispatcherImpl_8_0(AMQProtocolSession session) + { + super(session); + } + + public boolean dispatchBasicRecoverOk(BasicRecoverOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchChannelAlert(ChannelAlertBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContent(TestContentBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestContentOk(TestContentOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestInteger(TestIntegerBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestIntegerOk(TestIntegerOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestString(TestStringBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestStringOk(TestStringOkBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTable(TestTableBody body, int channelId) throws AMQException + { + return false; + } + + public boolean dispatchTestTableOk(TestTableOkBody body, int channelId) throws AMQException + { + return false; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java new file mode 100644 index 0000000000..bc82d6bc62 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.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.client.handler; + +import org.apache.qpid.AMQConnectionClosedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.protocol.AMQConstant; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConnectionCloseMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ConnectionCloseMethodHandler.class); + + private static ConnectionCloseMethodHandler _handler = new ConnectionCloseMethodHandler(); + + public static ConnectionCloseMethodHandler getInstance() + { + return _handler; + } + + private ConnectionCloseMethodHandler() + { + } + + public void methodReceived(AMQProtocolSession session, ConnectionCloseBody method, int channelId) + throws AMQException + { + _logger.info("ConnectionClose frame received"); + + // does it matter + // stateManager.changeState(AMQState.CONNECTION_CLOSING); + + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); + + AMQException error = null; + + try + { + + ConnectionCloseOkBody closeOkBody = session.getMethodRegistry().createConnectionCloseOkBody(); + // TODO: check whether channel id of zero is appropriate + // Be aware of possible changes to parameter order as versions change. + session.writeFrame(closeOkBody.generateFrame(0)); + + if (errorCode != AMQConstant.REPLY_SUCCESS) + { + if (errorCode == AMQConstant.NOT_ALLOWED || (errorCode == AMQConstant.ACCESS_REFUSED)) + { + _logger.info("Error :" + errorCode + ":" + Thread.currentThread().getName()); + + error = new AMQAuthenticationException(errorCode, reason == null ? null : reason.toString(), null); + } + else + { + _logger.info("Connection close received with error code " + errorCode); + + error = new AMQConnectionClosedException(errorCode, "Error: " + reason, null); + } + } + } + finally + { + + if (error != null) + { + session.notifyError(error); + } + + // Close the protocol Session, including any open TCP connections + session.closeProtocolSession(); + + // Closing the session should not introduce a race condition as this thread will continue to propgate any + // exception in to the exceptionCaught method of the SessionHandler. + // Any sessionClosed event should occur after this. + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java new file mode 100644 index 0000000000..e639a33450 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java @@ -0,0 +1,49 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.ConnectionOpenOkBody; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.StateAwareMethodListener; + +public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener +{ + private static final ConnectionOpenOkMethodHandler _instance = new ConnectionOpenOkMethodHandler(); + + public static ConnectionOpenOkMethodHandler getInstance() + { + return _instance; + } + + private ConnectionOpenOkMethodHandler() + { + } + + public void methodReceived(AMQProtocolSession session, ConnectionOpenOkBody body, int channelId) + throws AMQException + { + session.getStateManager().changeState(AMQState.CONNECTION_OPEN); + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java new file mode 100644 index 0000000000..472c471fd6 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java @@ -0,0 +1,71 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ConnectionRedirectBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConnectionRedirectMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ConnectionRedirectMethodHandler.class); + + private static final int DEFAULT_REDIRECT_PORT = 5672; + + private static ConnectionRedirectMethodHandler _handler = new ConnectionRedirectMethodHandler(); + + public static ConnectionRedirectMethodHandler getInstance() + { + return _handler; + } + + private ConnectionRedirectMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ConnectionRedirectBody method, int channelId) + throws AMQException + { + _logger.info("ConnectionRedirect frame received"); + + String host = method.getHost().toString(); + // the host is in the form hostname:port with the port being optional + int portIndex = host.indexOf(':'); + + int port; + if (portIndex == -1) + { + port = DEFAULT_REDIRECT_PORT; + } + else + { + port = Integer.parseInt(host.substring(portIndex + 1)); + host = host.substring(0, portIndex); + + } + + session.failover(host, port); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java new file mode 100644 index 0000000000..9a9bee757b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionSecureOkBody; + +public class ConnectionSecureMethodHandler implements StateAwareMethodListener +{ + private static final ConnectionSecureMethodHandler _instance = new ConnectionSecureMethodHandler(); + + public static ConnectionSecureMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQProtocolSession session, ConnectionSecureBody body, int channelId) + throws AMQException + { + SaslClient client = session.getSaslClient(); + if (client == null) + { + throw new AMQException(null, "No SASL client set up - cannot proceed with authentication", null); + } + + + + try + { + // Evaluate server challenge + byte[] response = client.evaluateChallenge(body.getChallenge()); + + ConnectionSecureOkBody secureOkBody = session.getMethodRegistry().createConnectionSecureOkBody(response); + + session.writeFrame(secureOkBody.generateFrame(channelId)); + } + catch (SaslException e) + { + throw new AMQException(null, "Error processing SASL challenge: " + e, e); + } + + + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java new file mode 100644 index 0000000000..8857f1115a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.security.AMQCallbackHandler; +import org.apache.qpid.client.security.CallbackHandlerRegistry; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.common.ClientProperties; +import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionStartBody; +import org.apache.qpid.framing.ConnectionStartOkBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.framing.ProtocolVersion; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import java.io.UnsupportedEncodingException; +import java.util.HashSet; +import java.util.StringTokenizer; + +public class ConnectionStartMethodHandler implements StateAwareMethodListener +{ + private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class); + + private static final ConnectionStartMethodHandler _instance = new ConnectionStartMethodHandler(); + + public static ConnectionStartMethodHandler getInstance() + { + return _instance; + } + + private ConnectionStartMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ConnectionStartBody body, int channelId) + throws AMQException + { + _log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, " + + "AMQMethodEvent evt): called"); + + ProtocolVersion pv = new ProtocolVersion((byte) body.getVersionMajor(), (byte) body.getVersionMinor()); + + // For the purposes of interop, we can make the client accept the broker's version string. + // If it does, it then internally records the version as being the latest one that it understands. + // It needs to do this since frame lookup is done by version. + if (Boolean.getBoolean("qpid.accept.broker.version") && !pv.isSupported()) + { + + pv = ProtocolVersion.getLatestSupportedVersion(); + } + + if (pv.isSupported()) + { + session.setProtocolVersion(pv); + + try + { + // Used to hold the SASL mechanism to authenticate with. + String mechanism; + + if (body.getMechanisms()== null) + { + throw new AMQException(null, "mechanism not specified in ConnectionStart method frame", null); + } + else + { + mechanism = chooseMechanism(body.getMechanisms()); + _log.debug("mechanism = " + mechanism); + } + + if (mechanism == null) + { + throw new AMQException(null, "No supported security mechanism found, passed: " + new String(body.getMechanisms()), null); + } + + byte[] saslResponse; + try + { + SaslClient sc = + Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null, + createCallbackHandler(mechanism, session)); + if (sc == null) + { + throw new AMQException(null, "Client SASL configuration error: no SaslClient could be created for mechanism " + mechanism + + ". Please ensure all factories are registered. See DynamicSaslRegistrar for " + + " details of how to register non-standard SASL client providers.", null); + } + + session.setSaslClient(sc); + saslResponse = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[0]) : null); + } + catch (SaslException e) + { + session.setSaslClient(null); + throw new AMQException(null, "Unable to create SASL client: " + e, e); + } + + if (body.getLocales() == null) + { + throw new AMQException(null, "Locales is not defined in Connection Start method", null); + } + + final String locales = new String(body.getLocales(), "utf8"); + final StringTokenizer tokenizer = new StringTokenizer(locales, " "); + String selectedLocale = null; + if (tokenizer.hasMoreTokens()) + { + selectedLocale = tokenizer.nextToken(); + } + else + { + throw new AMQException(null, "No locales sent from server, passed: " + locales, null); + } + + session.getStateManager().changeState(AMQState.CONNECTION_NOT_TUNED); + FieldTable clientProperties = FieldTableFactory.newFieldTable(); + + clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()), + session.getClientID()); + clientProperties.setString(new AMQShortString(ClientProperties.product.toString()), + QpidProperties.getProductName()); + clientProperties.setString(new AMQShortString(ClientProperties.version.toString()), + QpidProperties.getReleaseVersion()); + clientProperties.setString(new AMQShortString(ClientProperties.platform.toString()), getFullSystemInfo()); + + + ConnectionStartOkBody connectionStartOkBody = session.getMethodRegistry().createConnectionStartOkBody(clientProperties,new AMQShortString(mechanism),saslResponse,new AMQShortString(locales)); + // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) + // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. + // Be aware of possible changes to parameter order as versions change. + session.writeFrame(connectionStartOkBody.generateFrame(channelId)); + + } + catch (UnsupportedEncodingException e) + { + throw new AMQException(null, "Unable to decode data: " + e, e); + } + } + else + { + _log.error("Broker requested Protocol [" + body.getVersionMajor() + "-" + body.getVersionMinor() + + "] which is not supported by this version of the client library"); + + session.closeProtocolSession(); + } + } + + private String getFullSystemInfo() + { + StringBuffer fullSystemInfo = new StringBuffer(); + fullSystemInfo.append(System.getProperty("java.runtime.name")); + fullSystemInfo.append(", " + System.getProperty("java.runtime.version")); + fullSystemInfo.append(", " + System.getProperty("java.vendor")); + fullSystemInfo.append(", " + System.getProperty("os.arch")); + fullSystemInfo.append(", " + System.getProperty("os.name")); + fullSystemInfo.append(", " + System.getProperty("os.version")); + fullSystemInfo.append(", " + System.getProperty("sun.os.patch.level")); + + return fullSystemInfo.toString(); + } + + private String chooseMechanism(byte[] availableMechanisms) throws UnsupportedEncodingException + { + final String mechanisms = new String(availableMechanisms, "utf8"); + StringTokenizer tokenizer = new StringTokenizer(mechanisms, " "); + HashSet mechanismSet = new HashSet(); + while (tokenizer.hasMoreTokens()) + { + mechanismSet.add(tokenizer.nextToken()); + } + + String preferredMechanisms = CallbackHandlerRegistry.getInstance().getMechanisms(); + StringTokenizer prefTokenizer = new StringTokenizer(preferredMechanisms, " "); + while (prefTokenizer.hasMoreTokens()) + { + String mech = prefTokenizer.nextToken(); + if (mechanismSet.contains(mech)) + { + return mech; + } + } + + return null; + } + + private AMQCallbackHandler createCallbackHandler(String mechanism, AMQProtocolSession protocolSession) + throws AMQException + { + Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism); + try + { + Object instance = mechanismClass.newInstance(); + AMQCallbackHandler cbh = (AMQCallbackHandler) instance; + cbh.initialise(protocolSession); + + return cbh; + } + catch (Exception e) + { + throw new AMQException(null, "Unable to create callback handler: " + e, e); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java new file mode 100644 index 0000000000..e4e58c317d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.ConnectionTuneParameters; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConnectionTuneMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ConnectionTuneMethodHandler.class); + + private static final ConnectionTuneMethodHandler _instance = new ConnectionTuneMethodHandler(); + + public static ConnectionTuneMethodHandler getInstance() + { + return _instance; + } + + protected ConnectionTuneMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ConnectionTuneBody frame, int channelId) + throws AMQException + { + _logger.debug("ConnectionTune frame received"); + final MethodRegistry methodRegistry = session.getMethodRegistry(); + + + ConnectionTuneParameters params = session.getConnectionTuneParameters(); + if (params == null) + { + params = new ConnectionTuneParameters(); + } + + params.setFrameMax(frame.getFrameMax()); + params.setChannelMax(frame.getChannelMax()); + params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.getHeartbeat())); + session.setConnectionTuneParameters(params); + + session.getStateManager().changeState(AMQState.CONNECTION_NOT_OPENED); + + ConnectionTuneOkBody tuneOkBody = methodRegistry.createConnectionTuneOkBody(params.getChannelMax(), + params.getFrameMax(), + params.getHeartbeat()); + // Be aware of possible changes to parameter order as versions change. + session.writeFrame(tuneOkBody.generateFrame(channelId)); + + String host = session.getAMQConnection().getVirtualHost(); + AMQShortString virtualHost = new AMQShortString("/" + host); + + ConnectionOpenBody openBody = methodRegistry.createConnectionOpenBody(virtualHost,null,true); + + // Be aware of possible changes to parameter order as versions change. + session.writeFrame(openBody.generateFrame(channelId)); + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java new file mode 100644 index 0000000000..690d782b40 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ExchangeBoundOkBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Apache Software Foundation + */ +public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ExchangeBoundOkMethodHandler.class); + private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler(); + + public static ExchangeBoundOkMethodHandler getInstance() + { + return _instance; + } + + private ExchangeBoundOkMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, ExchangeBoundOkBody body, int channelId) + throws AMQException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.getReplyCode() + " text: " + + body.getReplyText()); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java new file mode 100644 index 0000000000..01d82c9b55 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.QueueDeleteOkBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Apache Software Foundation + */ +public class QueueDeleteOkMethodHandler implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(QueueDeleteOkMethodHandler.class); + private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler(); + + public static QueueDeleteOkMethodHandler getInstance() + { + return _instance; + } + + private QueueDeleteOkMethodHandler() + { } + + public void methodReceived(AMQProtocolSession session, QueueDeleteOkBody body, int channelId) + throws AMQException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Received Queue.Delete-Ok message, message count: " + body.getMessageCount()); + } + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java new file mode 100644 index 0000000000..7f43e4b4b2 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate.java @@ -0,0 +1,138 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.client.message; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +import javax.jms.Destination; +import javax.jms.JMSException; +import java.util.Enumeration; +import java.util.UUID; + +public interface AMQMessageDelegate +{ + void acknowledgeThis() throws JMSException; + + String getJMSMessageID() throws JMSException; + + void setJMSMessageID(String string) throws JMSException; + + long getJMSTimestamp() throws JMSException; + + void setJMSTimestamp(long l) throws JMSException; + + byte[] getJMSCorrelationIDAsBytes() throws JMSException; + + void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException; + + void setJMSCorrelationID(String string) throws JMSException; + + String getJMSCorrelationID() throws JMSException; + + Destination getJMSReplyTo() throws JMSException; + + void setJMSReplyTo(Destination destination) throws JMSException; + + Destination getJMSDestination() throws JMSException; + + int getJMSDeliveryMode() throws JMSException; + + void setJMSDeliveryMode(int i) throws JMSException; + + String getJMSType() throws JMSException; + + void setJMSType(String string) throws JMSException; + + long getJMSExpiration() throws JMSException; + + void setJMSExpiration(long l) throws JMSException; + + int getJMSPriority() throws JMSException; + + void setJMSPriority(int i) throws JMSException; + + void clearProperties() throws JMSException; + + boolean propertyExists(String string) throws JMSException; + + boolean getBooleanProperty(String string) throws JMSException; + + byte getByteProperty(String string) throws JMSException; + + short getShortProperty(String string) throws JMSException; + + int getIntProperty(String string) throws JMSException; + + long getLongProperty(String string) throws JMSException; + + float getFloatProperty(String string) throws JMSException; + + double getDoubleProperty(String string) throws JMSException; + + String getStringProperty(String string) throws JMSException; + + Object getObjectProperty(String string) throws JMSException; + + Enumeration getPropertyNames() throws JMSException; + + void setBooleanProperty(String string, boolean b) throws JMSException; + + void setByteProperty(String string, byte b) throws JMSException; + + void setShortProperty(String string, short i) throws JMSException; + + void setIntProperty(String string, int i) throws JMSException; + + void setLongProperty(String string, long l) throws JMSException; + + void setFloatProperty(String string, float v) throws JMSException; + + void setDoubleProperty(String string, double v) throws JMSException; + + void setStringProperty(String string, String string1) throws JMSException; + + void setObjectProperty(String string, Object object) throws JMSException; + + void acknowledge() throws JMSException; + + public void setJMSDestination(Destination destination); + + public void setContentType(String contentType); + public String getContentType(); + + public void setEncoding(String encoding); + public String getEncoding(); + + + String getReplyToString(); + + void removeProperty(final String propertyName) throws JMSException; + + void setAMQSession(final AMQSession s); + + AMQSession getAMQSession(); + + long getDeliveryTag(); + + void setJMSMessageID(final UUID messageId) throws JMSException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java new file mode 100644 index 0000000000..8c3f2fd08f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegateFactory.java @@ -0,0 +1,54 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.client.message; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.AMQException; + +public interface AMQMessageDelegateFactory +{ + public static AMQMessageDelegateFactory DEFAULT_FACTORY = null; + + public static AMQMessageDelegateFactory FACTORY_0_8 = + new AMQMessageDelegateFactory() + { + public AMQMessageDelegate_0_8 createDelegate() + { + return new AMQMessageDelegate_0_8(); + } + }; + + public static AMQMessageDelegateFactory FACTORY_0_10 = + new AMQMessageDelegateFactory() + { + public AMQMessageDelegate_0_10 createDelegate() + { + return new AMQMessageDelegate_0_10(); + } + }; + + + public D createDelegate(); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java new file mode 100644 index 0000000000..d064c27754 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java @@ -0,0 +1,972 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.client.message; + +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.qpid.client.*; +import org.apache.qpid.framing.ContentHeaderProperties; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQPInvalidClassException; +import org.apache.qpid.jms.Message; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.transport.*; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageNotWriteableException; +import javax.jms.MessageFormatException; +import javax.jms.DeliveryMode; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import org.apache.qpid.exchange.ExchangeDefaults; + +public class AMQMessageDelegate_0_10 implements AMQMessageDelegate +{ + private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); + + public static final String JMS_TYPE = "x-jms-type"; + + + private boolean _readableProperties = false; + + private Destination _destination; + + + private MessageProperties _messageProps; + private DeliveryProperties _deliveryProps; + /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ + private AMQSession _session; + private final long _deliveryTag; + + private static Map _exchangeTypeMap = new ConcurrentHashMap(); + private static Map _exchangeTypeStringMap = new ConcurrentHashMap(); + private static Map _exchangeTypeToDestinationType = new ConcurrentHashMap();; + + static + { + _exchangeTypeMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME, AMQDestination.QUEUE_TYPE); + _exchangeTypeMap.put(AMQShortString.EMPTY_STRING, AMQDestination.QUEUE_TYPE); + _exchangeTypeMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); + _exchangeTypeMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME, AMQDestination.TOPIC_TYPE); + + _exchangeTypeStringMap.put(ExchangeDefaults.DIRECT_EXCHANGE_NAME.toString(), AMQDestination.QUEUE_TYPE); + _exchangeTypeStringMap.put("", AMQDestination.QUEUE_TYPE); + _exchangeTypeStringMap.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + _exchangeTypeStringMap.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + + + _exchangeTypeToDestinationType.put(ExchangeDefaults.DIRECT_EXCHANGE_CLASS.toString(), AMQDestination.QUEUE_TYPE); + _exchangeTypeToDestinationType.put(ExchangeDefaults.TOPIC_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + _exchangeTypeToDestinationType.put(ExchangeDefaults.FANOUT_EXCHANGE_NAME.toString(), AMQDestination.TOPIC_TYPE); + } + + protected AMQMessageDelegate_0_10() + { + this(new MessageProperties(), new DeliveryProperties(), -1); + _readableProperties = false; + } + + protected AMQMessageDelegate_0_10(long deliveryTag, MessageProperties messageProps, DeliveryProperties deliveryProps, AMQShortString exchange, + AMQShortString routingKey) throws AMQException + { + this(messageProps, deliveryProps, deliveryTag); + + + AMQDestination dest; + + dest = generateDestination(exchange, routingKey); + setJMSDestination(dest); + } + + private AMQDestination generateDestination(AMQShortString exchange, AMQShortString routingKey) + { + AMQDestination dest; + switch(getExchangeType(exchange)) + { + case AMQDestination.QUEUE_TYPE: + dest = new AMQQueue(exchange, routingKey, routingKey); + break; + case AMQDestination.TOPIC_TYPE: + dest = new AMQTopic(exchange, routingKey, null); + break; + default: + dest = new AMQUndefinedDestination(exchange, routingKey, null); + + } + + return dest; + } + + private int getExchangeType(AMQShortString exchange) + { + Integer type = _exchangeTypeMap.get(exchange == null ? AMQShortString.EMPTY_STRING : exchange); + + if(type == null) + { + return AMQDestination.UNKNOWN_TYPE; + } + + + return type; + } + + + public static void updateExchangeTypeMapping(Header header, org.apache.qpid.transport.Session session) + { + DeliveryProperties deliveryProps = header.get(DeliveryProperties.class); + if(deliveryProps != null) + { + String exchange = deliveryProps.getExchange(); + + if(exchange != null && !_exchangeTypeStringMap.containsKey(exchange)) + { + + AMQShortString exchangeShortString = new AMQShortString(exchange); + Future future = + session.exchangeQuery(exchange.toString()); + ExchangeQueryResult res = future.get(); + + Integer type = _exchangeTypeToDestinationType.get(res.getType()); + if(type == null) + { + type = AMQDestination.UNKNOWN_TYPE; + } + _exchangeTypeStringMap.put(exchange, type); + _exchangeTypeMap.put(exchangeShortString, type); + + } + } + } + + protected AMQMessageDelegate_0_10(MessageProperties messageProps, DeliveryProperties deliveryProps, long deliveryTag) + { + _messageProps = messageProps; + _deliveryProps = deliveryProps; + _deliveryTag = deliveryTag; + _readableProperties = (_messageProps != null); + + } + + + public String getJMSMessageID() throws JMSException + { + UUID id = _messageProps.getMessageId(); + return id == null ? null : "ID:" + id; + } + + public void setJMSMessageID(String messageId) throws JMSException + { + if(messageId == null) + { + _messageProps.clearMessageId(); + } + else + { + if(messageId.startsWith("ID:")) + { + try + { + _messageProps.setMessageId(UUID.fromString(messageId.substring(3))); + } + catch(IllegalArgumentException ex) + { + throw new JMSException("MessageId '"+messageId+"' is not of the correct format, it must be ID: followed by a UUID"); + } + } + else + { + throw new JMSException("MessageId '"+messageId+"' is not of the correct format, it must be ID: followed by a UUID"); + } + } + } + + public void setJMSMessageID(UUID messageId) throws JMSException + { + if(messageId == null) + { + _messageProps.clearMessageId(); + } + else + { + _messageProps.setMessageId(messageId); + } + } + + + public long getJMSTimestamp() throws JMSException + { + return _deliveryProps.getTimestamp(); + } + + public void setJMSTimestamp(long timestamp) throws JMSException + { + _deliveryProps.setTimestamp(timestamp); + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException + { + return _messageProps.getCorrelationId(); + } + + public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException + { + _messageProps.setCorrelationId(bytes); + } + + public void setJMSCorrelationID(String correlationId) throws JMSException + { + + setJMSCorrelationIDAsBytes(correlationId == null ? null : correlationId.getBytes()); + } + + public String getJMSCorrelationID() throws JMSException + { + + byte[] correlationIDAsBytes = getJMSCorrelationIDAsBytes(); + return correlationIDAsBytes == null ? null : new String(correlationIDAsBytes); + } + + public Destination getJMSReplyTo() + { + ReplyTo replyTo = _messageProps.getReplyTo(); + + if (replyTo == null) + { + return null; + } + else + { + Destination dest = _destinationCache.get(replyTo); + if (dest == null) + { + String exchange = replyTo.getExchange(); + String routingKey = replyTo.getRoutingKey(); + + dest = generateDestination(exchange == null ? null : new AMQShortString(exchange), + routingKey == null ? null : new AMQShortString(routingKey)); + + + + + + _destinationCache.put(replyTo, dest); + } + + return dest; + } + } + + public void setJMSReplyTo(Destination destination) throws JMSException + { + if (destination == null) + { + throw new IllegalArgumentException("Null destination not allowed"); + } + + if (!(destination instanceof AMQDestination)) + { + throw new IllegalArgumentException( + "ReplyTo destination may only be an AMQDestination - passed argument was type " + destination.getClass()); + } + + final AMQDestination amqd = (AMQDestination) destination; + + final ReplyTo replyTo = new ReplyTo(amqd.getExchangeName().toString(), amqd.getRoutingKey().toString()); + _destinationCache.put(replyTo, destination); + _messageProps.setReplyTo(replyTo); + + } + + public Destination getJMSDestination() throws JMSException + { + return _destination; + } + + public void setJMSDestination(Destination destination) + { + _destination = destination; + } + + public void setContentType(String contentType) + { + _messageProps.setContentType(contentType); + } + + public String getContentType() + { + return _messageProps.getContentType(); + } + + public void setEncoding(String encoding) + { + if(encoding == null || encoding.length() == 0) + { + _messageProps.clearContentEncoding(); + } + else + { + _messageProps.setContentEncoding(encoding); + } + } + + public String getEncoding() + { + return _messageProps.getContentEncoding(); + } + + public String getReplyToString() + { + Destination replyTo = getJMSReplyTo(); + if(replyTo != null) + { + return ((AMQDestination)replyTo).toURL(); + } + else + { + return null; + } + + } + + public int getJMSDeliveryMode() throws JMSException + { + + MessageDeliveryMode deliveryMode = _deliveryProps.getDeliveryMode(); + if(deliveryMode != null) + { + switch(deliveryMode) + { + case PERSISTENT : + return DeliveryMode.PERSISTENT; + case NON_PERSISTENT: + return DeliveryMode.NON_PERSISTENT; + default: + throw new JMSException("Unknown Message Delivery Mode: " + _deliveryProps.getDeliveryMode()); + } + } + else + { + return Message.DEFAULT_DELIVERY_MODE; + } + + } + + public void setJMSDeliveryMode(int deliveryMode) throws JMSException + { + switch(deliveryMode) + { + case DeliveryMode.PERSISTENT: + _deliveryProps.setDeliveryMode(MessageDeliveryMode.PERSISTENT); + break; + case DeliveryMode.NON_PERSISTENT: + _deliveryProps.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); + break; + default: + throw new JMSException("Unknown JMS Delivery Mode: " + deliveryMode); + } + + } + + + public String getJMSType() throws JMSException + { + if(getApplicationHeaders().containsKey(JMS_TYPE)) + { + return getStringProperty(JMS_TYPE); + } + else + { + return null; + } + } + + private Map getApplicationHeaders() + { + Map map = _messageProps.getApplicationHeaders(); + return map == null ? Collections.EMPTY_MAP : map; + } + + public void setJMSType(String type) throws JMSException + { + Map headers = _messageProps.getApplicationHeaders(); + if(type == null) + { + if(headers != null) + { + headers.remove(JMS_TYPE); + } + } + else + { + if(headers == null) + { + headers = new HashMap(); + _messageProps.setApplicationHeaders(headers); + + } + headers.put(JMS_TYPE, type); + } + } + + public long getJMSExpiration() throws JMSException + { + return _deliveryProps.getExpiration(); + } + + public void setJMSExpiration(long l) throws JMSException + { + _deliveryProps.setExpiration(l); + } + + + + public boolean propertyExists(String propertyName) throws JMSException + { + return getApplicationHeaders().containsKey(propertyName); + } + + public boolean getBooleanProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Object o = getApplicationHeaders().get(propertyName); + + if(o instanceof Boolean) + { + return ((Boolean)o).booleanValue(); + } + else if(o instanceof String) + { + return Boolean.valueOf((String) o).booleanValue(); + } + else if(getApplicationHeaders().containsKey(propertyName)) + { + throw new MessageFormatException("getBooleanProperty(\""+propertyName+"\") failed as value is not boolean: " + o); + } + else + { + return Boolean.valueOf(null); + } + } + + public byte getByteProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Byte) + { + return ((Byte)o).byteValue(); + } + else if(o instanceof String) + { + return Byte.valueOf((String) o).byteValue(); + } + else if(getApplicationHeaders().containsKey(propertyName)) + { + throw new MessageFormatException("getByteProperty(\""+propertyName+"\") failed as value is not a byte: " + o); + } + else + { + return Byte.valueOf(null); + } + } + + public short getShortProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Short) + { + return ((Short)o).shortValue(); + } + else + { + try + { + return Short.valueOf(getByteProperty(propertyName)); + } + catch(MessageFormatException e) + { + throw new MessageFormatException("getShortProperty(\""+propertyName+"\") failed as value is not a short: " + o); + } + } + + + } + + public int getIntProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Integer) + { + return ((Integer)o).intValue(); + } + else + { + try + { + return Integer.valueOf(getShortProperty(propertyName)); + } + catch(MessageFormatException e) + { + throw new MessageFormatException("getIntProperty(\""+propertyName+"\") failed as value is not an int: " + o); + } + + } + } + + public long getLongProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Long) + { + return ((Long)o).longValue(); + } + else + { + try + { + return Long.valueOf(getIntProperty(propertyName)); + } + catch(MessageFormatException e) + { + throw new MessageFormatException("getLongProperty(\""+propertyName+"\") failed as value is not a long: " + o); + } + + } + } + + public float getFloatProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Float) + { + return ((Float)o).floatValue(); + } + else if(o instanceof String) + { + return Float.valueOf((String) o).floatValue(); + } + else if(getApplicationHeaders().containsKey(propertyName)) + { + throw new MessageFormatException("getFloatProperty(\""+propertyName+"\") failed as value is not a float: " + o); + } + else + { + return Float.valueOf(null); + } + + } + + public double getDoubleProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof Double) + { + return ((Double)o).doubleValue(); + } + else + { + try + { + return Double.valueOf(getFloatProperty(propertyName)); + } + catch(MessageFormatException e) + { + throw new MessageFormatException("getDoubleProperty(\""+propertyName+"\") failed as value is not a double: " + o); + } + + } + } + + public String getStringProperty(String propertyName) throws JMSException + { + if (propertyName.equals(CustomJMSXProperty.JMSXUserID.toString())) + { + return new String(_messageProps.getUserId()); + } + else + { + checkPropertyName(propertyName); + Map propertyMap = getApplicationHeaders(); + + Object o = propertyMap.get(propertyName); + + if(o instanceof String) + { + return (String) o; + } + else if(o == null) + { + return null; + } + else if(o.getClass().isArray()) + { + throw new MessageFormatException("getString(\""+propertyName+"\") failed as value of type " + o.getClass()+ " is an array."); + } + else + { + return String.valueOf(o); + } + + } + } + + public Object getObjectProperty(String propertyName) throws JMSException + { + checkPropertyName(propertyName); + Map propertyMap = getApplicationHeaders(); + + return propertyMap.get(propertyName); + + } + + public Enumeration getPropertyNames() throws JMSException + { + return java.util.Collections.enumeration(getApplicationHeaders().keySet()); + } + + public void setBooleanProperty(String propertyName, boolean b) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, b); + } + + public void setByteProperty(String propertyName, byte b) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, b); + } + + public void setShortProperty(String propertyName, short i) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, i); + } + + public void setIntProperty(String propertyName, int i) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, i); + } + + public void setLongProperty(String propertyName, long l) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, l); + } + + public void setFloatProperty(String propertyName, float f) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, f); + } + + public void setDoubleProperty(String propertyName, double v) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, v); + } + + public void setStringProperty(String propertyName, String value) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + setApplicationHeader(propertyName, value); + } + + private static final Set ALLOWED = new HashSet(); + static + { + ALLOWED.add(Boolean.class); + ALLOWED.add(Byte.class); + ALLOWED.add(Short.class); + ALLOWED.add(Integer.class); + ALLOWED.add(Long.class); + ALLOWED.add(Float.class); + ALLOWED.add(Double.class); + ALLOWED.add(Character.class); + ALLOWED.add(String.class); + ALLOWED.add(byte[].class); + } + + public void setObjectProperty(String propertyName, Object object) throws JMSException + { + checkPropertyName(propertyName); + checkWritableProperties(); + if (object != null && !ALLOWED.contains(object.getClass())) + { + throw new MessageFormatException + (String.format + ("Cannot set a %s, allowed property types are: %s", + object.getClass(), ALLOWED)); + } + setApplicationHeader(propertyName, object); + } + + private void setApplicationHeader(String propertyName, Object object) + { + Map headers = _messageProps.getApplicationHeaders(); + if(headers == null) + { + headers = new HashMap(); + _messageProps.setApplicationHeaders(headers); + } + headers.put(propertyName, object); + } + + public void removeProperty(String propertyName) throws JMSException + { + Map headers = _messageProps.getApplicationHeaders(); + if(headers != null) + { + headers.remove(propertyName); + } + } + + + + protected void checkWritableProperties() throws MessageNotWriteableException + { + if (_readableProperties) + { + throw new MessageNotWriteableException("You need to call clearProperties() to make the message writable"); + } + } + + + public int getJMSPriority() throws JMSException + { + MessageDeliveryPriority messageDeliveryPriority = _deliveryProps.getPriority(); + return messageDeliveryPriority == null ? Message.DEFAULT_PRIORITY : messageDeliveryPriority.getValue(); + } + + public void setJMSPriority(int i) throws JMSException + { + _deliveryProps.setPriority(MessageDeliveryPriority.get((short)i)); + } + + public void clearProperties() throws JMSException + { + if(!getApplicationHeaders().isEmpty()) + { + getApplicationHeaders().clear(); + } + + _readableProperties = false; + } + + + public void acknowledgeThis() throws JMSException + { + // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge + // is not specified. In our case, we only set the session field where client acknowledge mode is specified. + if (_session != null) + { + if (_session.getAMQConnection().isClosed()) + { + throw new javax.jms.IllegalStateException("Connection is already closed"); + } + + // we set multiple to true here since acknowledgement implies acknowledge of all previous messages + // received on the session + _session.acknowledgeMessage(_deliveryTag, true); + } + } + + public void acknowledge() throws JMSException + { + if (_session != null) + { + _session.acknowledge(); + } + } + + + /** + * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls + * acknowledge() + * + * @param s the AMQ session that delivered this message + */ + public void setAMQSession(AMQSession s) + { + _session = s; + } + + public AMQSession getAMQSession() + { + return _session; + } + + /** + * Get the AMQ message number assigned to this message + * + * @return the message number + */ + public long getDeliveryTag() + { + return _deliveryTag; + } + + + + + + + protected void checkPropertyName(CharSequence propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + checkIdentiferFormat(propertyName); + } + + protected void checkIdentiferFormat(CharSequence propertyName) + { +// JMS requirements 3.5.1 Property Names +// Identifiers: +// - An identifier is an unlimited-length character sequence that must begin +// with a Java identifier start character; all following characters must be Java +// identifier part characters. An identifier start character is any character for +// which the method Character.isJavaIdentifierStart returns true. This includes +// '_' and '$'. An identifier part character is any character for which the +// method Character.isJavaIdentifierPart returns true. +// - Identifiers cannot be the names NULL, TRUE, or FALSE. +// Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or +// ESCAPE. +// Identifiers are either header field references or property references. The +// type of a property value in a message selector corresponds to the type +// used to set the property. If a property that does not exist in a message is +// referenced, its value is NULL. The semantics of evaluating NULL values +// in a selector are described in Section 3.8.1.2, Null Values. +// The conversions that apply to the get methods for properties do not +// apply when a property is used in a message selector expression. For +// example, suppose you set a property as a string value, as in the +// following: +// myMessage.setStringProperty("NumberOfOrders", "2"); +// The following expression in a message selector would evaluate to false, +// because a string cannot be used in an arithmetic expression: +// "NumberOfOrders > 1" +// Identifiers are case sensitive. +// Message header field references are restricted to JMSDeliveryMode, +// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and +// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be +// null and if so are treated as a NULL value. + + if (Boolean.getBoolean("strict-jms")) + { + // JMS start character + if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); + } + + // JMS part character + int length = propertyName.length(); + for (int c = 1; c < length; c++) + { + if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); + } + } + + // JMS invalid names + if ((propertyName.equals("NULL") + || propertyName.equals("TRUE") + || propertyName.equals("FALSE") + || propertyName.equals("NOT") + || propertyName.equals("AND") + || propertyName.equals("OR") + || propertyName.equals("BETWEEN") + || propertyName.equals("LIKE") + || propertyName.equals("IN") + || propertyName.equals("IS") + || propertyName.equals("ESCAPE"))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); + } + } + + } + + + public MessageProperties getMessageProperties() + { + return _messageProps; + } + + + public DeliveryProperties getDeliveryProperties() + { + return _deliveryProps; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java new file mode 100644 index 0000000000..4c20a44849 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_8.java @@ -0,0 +1,567 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.client.message; + +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.CustomJMSXProperty; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.AMQUndefinedDestination; +import org.apache.qpid.client.JMSAMQException; +import org.apache.qpid.framing.ContentHeaderProperties; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.AMQException; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.AMQBindingURL; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageNotWriteableException; +import java.util.Map; +import java.util.Collections; +import java.util.Enumeration; +import java.util.UUID; +import java.net.URISyntaxException; + +public class AMQMessageDelegate_0_8 implements AMQMessageDelegate +{ + private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); + + public static final String JMS_TYPE = "x-jms-type"; + + + private boolean _readableProperties = false; + + private Destination _destination; + private JMSHeaderAdapter _headerAdapter; + private static final boolean STRICT_AMQP_COMPLIANCE = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); + + private ContentHeaderProperties _contentHeaderProperties; + /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ + private AMQSession _session; + private final long _deliveryTag; + + protected AMQMessageDelegate_0_8() + { + this(new BasicContentHeaderProperties(), -1); + _readableProperties = false; + _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); + + } + + protected AMQMessageDelegate_0_8(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, + AMQShortString routingKey) + { + this(contentHeader, deliveryTag); + + Integer type = contentHeader.getHeaders().getInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName()); + + if(type == null) + { + type = AMQDestination.UNKNOWN_TYPE; + } + + AMQDestination dest; + + switch(type.intValue()) + { + case AMQDestination.QUEUE_TYPE: + dest = new AMQQueue(exchange, routingKey, routingKey); + break; + case AMQDestination.TOPIC_TYPE: + dest = new AMQTopic(exchange, routingKey, null); + break; + default: + dest = new AMQUndefinedDestination(exchange, routingKey, null); + } + + + + // Destination dest = AMQDestination.createDestination(url); + setJMSDestination(dest); + + + + } + + protected AMQMessageDelegate_0_8(BasicContentHeaderProperties properties, long deliveryTag) + { + _contentHeaderProperties = properties; + _deliveryTag = deliveryTag; + _readableProperties = (_contentHeaderProperties != null); + _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); + } + + + public String getJMSMessageID() throws JMSException + { + return getContentHeaderProperties().getMessageIdAsString(); + } + + public void setJMSMessageID(String messageId) throws JMSException + { + getContentHeaderProperties().setMessageId(messageId); + } + + public void setJMSMessageID(UUID messageId) throws JMSException + { + getContentHeaderProperties().setMessageId("ID:" + messageId); + } + + + public long getJMSTimestamp() throws JMSException + { + return getContentHeaderProperties().getTimestamp(); + } + + public void setJMSTimestamp(long timestamp) throws JMSException + { + getContentHeaderProperties().setTimestamp(timestamp); + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException + { + return getContentHeaderProperties().getCorrelationIdAsString().getBytes(); + } + + public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException + { + getContentHeaderProperties().setCorrelationId(new String(bytes)); + } + + public void setJMSCorrelationID(String correlationId) throws JMSException + { + getContentHeaderProperties().setCorrelationId(correlationId); + } + + public String getJMSCorrelationID() throws JMSException + { + return getContentHeaderProperties().getCorrelationIdAsString(); + } + + public Destination getJMSReplyTo() throws JMSException + { + String replyToEncoding = getContentHeaderProperties().getReplyToAsString(); + if (replyToEncoding == null) + { + return null; + } + else + { + Destination dest = (Destination) _destinationCache.get(replyToEncoding); + if (dest == null) + { + try + { + BindingURL binding = new AMQBindingURL(replyToEncoding); + dest = AMQDestination.createDestination(binding); + } + catch (URISyntaxException e) + { + throw new JMSAMQException("Illegal value in JMS_ReplyTo property: " + replyToEncoding, e); + } + + _destinationCache.put(replyToEncoding, dest); + } + + return dest; + } + } + + public void setJMSReplyTo(Destination destination) throws JMSException + { + if (destination == null) + { + throw new IllegalArgumentException("Null destination not allowed"); + } + + if (!(destination instanceof AMQDestination)) + { + throw new IllegalArgumentException( + "ReplyTo destination may only be an AMQDestination - passed argument was type " + destination.getClass()); + } + + final AMQDestination amqd = (AMQDestination) destination; + + final AMQShortString encodedDestination = amqd.getEncodedName(); + _destinationCache.put(encodedDestination, destination); + getContentHeaderProperties().setReplyTo(encodedDestination); + } + + public Destination getJMSDestination() throws JMSException + { + return _destination; + } + + public void setJMSDestination(Destination destination) + { + _destination = destination; + } + + public void setContentType(String contentType) + { + getContentHeaderProperties().setContentType(contentType); + } + + public String getContentType() + { + return getContentHeaderProperties().getContentTypeAsString(); + } + + public void setEncoding(String encoding) + { + getContentHeaderProperties().setEncoding(encoding); + } + + public String getEncoding() + { + return getContentHeaderProperties().getEncodingAsString(); + } + + public String getReplyToString() + { + return getContentHeaderProperties().getReplyToAsString(); + } + + public int getJMSDeliveryMode() throws JMSException + { + return getContentHeaderProperties().getDeliveryMode(); + } + + public void setJMSDeliveryMode(int i) throws JMSException + { + getContentHeaderProperties().setDeliveryMode((byte) i); + } + + public BasicContentHeaderProperties getContentHeaderProperties() + { + return (BasicContentHeaderProperties) _contentHeaderProperties; + } + + + public String getJMSType() throws JMSException + { + return getContentHeaderProperties().getTypeAsString(); + } + + public void setJMSType(String string) throws JMSException + { + getContentHeaderProperties().setType(string); + } + + public long getJMSExpiration() throws JMSException + { + return getContentHeaderProperties().getExpiration(); + } + + public void setJMSExpiration(long l) throws JMSException + { + getContentHeaderProperties().setExpiration(l); + } + + + + public boolean propertyExists(String propertyName) throws JMSException + { + return getJmsHeaders().propertyExists(propertyName); + } + + public boolean getBooleanProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getBoolean(propertyName); + } + + public byte getByteProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getByte(propertyName); + } + + public short getShortProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getShort(propertyName); + } + + public int getIntProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getInteger(propertyName); + } + + public long getLongProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getLong(propertyName); + } + + public float getFloatProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getFloat(propertyName); + } + + public double getDoubleProperty(String propertyName) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getDouble(propertyName); + } + + public String getStringProperty(String propertyName) throws JMSException + { + //NOTE: if the JMSX Property is a non AMQP property then we must check _strictAMQP and throw as below. + if (propertyName.equals(CustomJMSXProperty.JMSXUserID.toString())) + { + return ((BasicContentHeaderProperties) _contentHeaderProperties).getUserIdAsString(); + } + else + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getString(propertyName); + } + } + + public Object getObjectProperty(String propertyName) throws JMSException + { + return getJmsHeaders().getObject(propertyName); + } + + public Enumeration getPropertyNames() throws JMSException + { + return getJmsHeaders().getPropertyNames(); + } + + public void setBooleanProperty(String propertyName, boolean b) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setBoolean(propertyName, b); + } + + public void setByteProperty(String propertyName, byte b) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setByte(propertyName, new Byte(b)); + } + + public void setShortProperty(String propertyName, short i) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setShort(propertyName, new Short(i)); + } + + public void setIntProperty(String propertyName, int i) throws JMSException + { + checkWritableProperties(); + getJmsHeaders().setInteger(propertyName, new Integer(i)); + } + + public void setLongProperty(String propertyName, long l) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setLong(propertyName, new Long(l)); + } + + public void setFloatProperty(String propertyName, float f) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setFloat(propertyName, new Float(f)); + } + + public void setDoubleProperty(String propertyName, double v) throws JMSException + { + if (STRICT_AMQP_COMPLIANCE) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setDouble(propertyName, new Double(v)); + } + + public void setStringProperty(String propertyName, String value) throws JMSException + { + checkWritableProperties(); + getJmsHeaders().setString(propertyName, value); + } + + public void setObjectProperty(String propertyName, Object object) throws JMSException + { + checkWritableProperties(); + getJmsHeaders().setObject(propertyName, object); + } + + public void removeProperty(String propertyName) throws JMSException + { + getJmsHeaders().remove(propertyName); + } + + + private JMSHeaderAdapter getJmsHeaders() + { + return _headerAdapter; + } + + protected void checkWritableProperties() throws MessageNotWriteableException + { + if (_readableProperties) + { + throw new MessageNotWriteableException("You need to call clearProperties() to make the message writable"); + } + _contentHeaderProperties.updated(); + } + + + public int getJMSPriority() throws JMSException + { + return getContentHeaderProperties().getPriority(); + } + + public void setJMSPriority(int i) throws JMSException + { + getContentHeaderProperties().setPriority((byte) i); + } + + public void clearProperties() throws JMSException + { + getJmsHeaders().clear(); + + _readableProperties = false; + } + + + public void acknowledgeThis() throws JMSException + { + // the JMS 1.1 spec says in section 3.6 that calls to acknowledge are ignored when client acknowledge + // is not specified. In our case, we only set the session field where client acknowledge mode is specified. + if (_session != null) + { + if (_session.getAMQConnection().isClosed()) + { + throw new javax.jms.IllegalStateException("Connection is already closed"); + } + + // we set multiple to true here since acknowledgement implies acknowledge of all previous messages + // received on the session + _session.acknowledgeMessage(_deliveryTag, true); + } + } + + public void acknowledge() throws JMSException + { + if (_session != null) + { + _session.acknowledge(); + } + } + + + /** + * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls + * acknowledge() + * + * @param s the AMQ session that delivered this message + */ + public void setAMQSession(AMQSession s) + { + _session = s; + } + + public AMQSession getAMQSession() + { + return _session; + } + + /** + * Get the AMQ message number assigned to this message + * + * @return the message number + */ + public long getDeliveryTag() + { + return _deliveryTag; + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java new file mode 100644 index 0000000000..c0d51fa726 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java @@ -0,0 +1,149 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.io.IOException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +/** + * @author Apache Software Foundation + */ +public abstract class AbstractBytesMessage extends AbstractJMSMessage +{ + + /** + * The default initial size of the buffer. The buffer expands automatically. + */ + private static final int DEFAULT_BUFFER_INITIAL_SIZE = 1024; + + AbstractBytesMessage(AMQMessageDelegateFactory delegateFactory) + { + this(delegateFactory, null); + } + + /** + * Construct a bytes message with existing data. + * + * @param delegateFactory + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + */ + AbstractBytesMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + super(delegateFactory, data); // this instanties a content header + setContentType(getMimeType()); + + if (_data == null) + { + allocateInitialBuffer(); + } + } + + protected void allocateInitialBuffer() + { + _data = ByteBuffer.allocate(DEFAULT_BUFFER_INITIAL_SIZE); + _data.setAutoExpand(true); + } + + AbstractBytesMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + super(delegate, data); + setContentType(getMimeType()); + } + + + public void clearBodyImpl() throws JMSException + { + allocateInitialBuffer(); + } + + public String toBodyString() throws JMSException + { + checkReadable(); + try + { + return getText(); + } + catch (IOException e) + { + JMSException jmse = new JMSException(e.toString()); + jmse.setLinkedException(e); + throw jmse; + } + } + + /** + * We reset the stream before and after reading the data. This means that toString() will always output + * the entire message and also that the caller can then immediately start reading as if toString() had + * never been called. + * + * @return + * @throws IOException + */ + private String getText() throws IOException + { + // this will use the default platform encoding + if (_data == null) + { + return null; + } + + int pos = _data.position(); + _data.rewind(); + // one byte left is for the end of frame marker + if (_data.remaining() == 0) + { + // this is really redundant since pos must be zero + _data.position(pos); + + return null; + } + else + { + String data = _data.getString(Charset.forName("UTF8").newDecoder()); + _data.position(pos); + + return data; + } + } + + /** + * Check that there is at least a certain number of bytes available to read + * + * @param len the number of bytes + * @throws javax.jms.MessageEOFException if there are less than len bytes available to read + */ + protected void checkAvailable(int len) throws MessageEOFException + { + if (_data.remaining() < len) + { + throw new MessageEOFException("Unable to read " + len + " bytes"); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java new file mode 100644 index 0000000000..f0fc394b0b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java @@ -0,0 +1,802 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.client.message; + +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +/** + * @author Apache Software Foundation + */ +public abstract class AbstractBytesTypedMessage extends AbstractBytesMessage +{ + + protected static final byte BOOLEAN_TYPE = (byte) 1; + + protected static final byte BYTE_TYPE = (byte) 2; + + protected static final byte BYTEARRAY_TYPE = (byte) 3; + + protected static final byte SHORT_TYPE = (byte) 4; + + protected static final byte CHAR_TYPE = (byte) 5; + + protected static final byte INT_TYPE = (byte) 6; + + protected static final byte LONG_TYPE = (byte) 7; + + protected static final byte FLOAT_TYPE = (byte) 8; + + protected static final byte DOUBLE_TYPE = (byte) 9; + + protected static final byte STRING_TYPE = (byte) 10; + + protected static final byte NULL_STRING_TYPE = (byte) 11; + + /** + * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read + * a byte array in multiple chunks, hence this is used to track how much is left to be read + */ + private int _byteArrayRemaining = -1; + + AbstractBytesTypedMessage(AMQMessageDelegateFactory delegateFactory) + { + + this(delegateFactory, null); + } + + /** + * Construct a stream message with existing data. + * + * @param delegateFactory + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + */ + AbstractBytesTypedMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + + super(delegateFactory, data); // this instanties a content header + } + + AbstractBytesTypedMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + + super(delegate, data); + } + + + protected byte readWireType() throws MessageFormatException, MessageEOFException, + MessageNotReadableException + { + checkReadable(); + checkAvailable(1); + return _data.get(); + } + + protected void writeTypeDiscriminator(byte type) throws MessageNotWriteableException + { + checkWritable(); + _data.put(type); + _changedData = true; + } + + protected boolean readBoolean() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + boolean result; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Boolean.parseBoolean(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a boolean"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private boolean readBooleanImpl() + { + return _data.get() != 0; + } + + protected byte readByte() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + byte result; + try + { + switch (wireType) + { + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Byte.parseByte(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a byte"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private byte readByteImpl() + { + return _data.get(); + } + + protected short readShort() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + short result; + try + { + switch (wireType) + { + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Short.parseShort(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a short"); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + return result; + } + + private short readShortImpl() + { + return _data.getShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws javax.jms.JMSException + */ + protected char readChar() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + try + { + if(wireType == NULL_STRING_TYPE){ + throw new NullPointerException(); + } + + if (wireType != CHAR_TYPE) + { + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a char"); + } + else + { + checkAvailable(2); + return readCharImpl(); + } + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private char readCharImpl() + { + return _data.getChar(); + } + + protected int readInt() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + int result; + try + { + switch (wireType) + { + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Integer.parseInt(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to an int"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected int readIntImpl() + { + return _data.getInt(); + } + + protected long readLong() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + long result; + try + { + switch (wireType) + { + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Long.parseLong(readStringImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a long"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private long readLongImpl() + { + return _data.getLong(); + } + + protected float readFloat() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + float result; + try + { + switch (wireType) + { + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Float.parseFloat(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a float"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private float readFloatImpl() + { + return _data.getFloat(); + } + + protected double readDouble() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + double result; + try + { + switch (wireType) + { + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case STRING_TYPE: + checkAvailable(1); + result = Double.parseDouble(readStringImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a double"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + private double readDoubleImpl() + { + return _data.getDouble(); + } + + protected String readString() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + String result; + try + { + switch (wireType) + { + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + case NULL_STRING_TYPE: + result = null; + throw new NullPointerException("data is null"); + case BOOLEAN_TYPE: + checkAvailable(1); + result = String.valueOf(readBooleanImpl()); + break; + case LONG_TYPE: + checkAvailable(8); + result = String.valueOf(readLongImpl()); + break; + case INT_TYPE: + checkAvailable(4); + result = String.valueOf(readIntImpl()); + break; + case SHORT_TYPE: + checkAvailable(2); + result = String.valueOf(readShortImpl()); + break; + case BYTE_TYPE: + checkAvailable(1); + result = String.valueOf(readByteImpl()); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = String.valueOf(readFloatImpl()); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = String.valueOf(readDoubleImpl()); + break; + case CHAR_TYPE: + checkAvailable(2); + result = String.valueOf(readCharImpl()); + break; + default: + _data.position(position); + throw new MessageFormatException("Unable to convert " + wireType + " to a String"); + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected String readStringImpl() throws JMSException + { + try + { + return _data.getString(Charset.forName("UTF-8").newDecoder()); + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + je.setLinkedException(e); + throw je; + } + } + + protected int readBytes(byte[] bytes) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + checkReadable(); + // first call + if (_byteArrayRemaining == -1) + { + // type discriminator checked separately so you get a MessageFormatException rather than + // an EOF even in the case where both would be applicable + checkAvailable(1); + byte wireType = readWireType(); + if (wireType != BYTEARRAY_TYPE) + { + throw new MessageFormatException("Unable to convert " + wireType + " to a byte array"); + } + checkAvailable(4); + int size = _data.getInt(); + // length of -1 indicates null + if (size == -1) + { + return -1; + } + else + { + if (size > _data.remaining()) + { + throw new MessageEOFException("Byte array has stated length " + size + " but message only contains " + + _data.remaining() + " bytes"); + } + else + { + _byteArrayRemaining = size; + } + } + } + else if (_byteArrayRemaining == 0) + { + _byteArrayRemaining = -1; + return -1; + } + + int returnedSize = readBytesImpl(bytes); + if (returnedSize < bytes.length) + { + _byteArrayRemaining = -1; + } + return returnedSize; + } + + private int readBytesImpl(byte[] bytes) + { + int count = (_byteArrayRemaining >= bytes.length ? bytes.length : _byteArrayRemaining); + _byteArrayRemaining -= count; + + if (count == 0) + { + return 0; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + protected Object readObject() throws JMSException + { + int position = _data.position(); + byte wireType = readWireType(); + Object result = null; + try + { + switch (wireType) + { + case BOOLEAN_TYPE: + checkAvailable(1); + result = readBooleanImpl(); + break; + case BYTE_TYPE: + checkAvailable(1); + result = readByteImpl(); + break; + case BYTEARRAY_TYPE: + checkAvailable(4); + int size = _data.getInt(); + if (size == -1) + { + result = null; + } + else + { + _byteArrayRemaining = size; + byte[] bytesResult = new byte[size]; + readBytesImpl(bytesResult); + result = bytesResult; + } + break; + case SHORT_TYPE: + checkAvailable(2); + result = readShortImpl(); + break; + case CHAR_TYPE: + checkAvailable(2); + result = readCharImpl(); + break; + case INT_TYPE: + checkAvailable(4); + result = readIntImpl(); + break; + case LONG_TYPE: + checkAvailable(8); + result = readLongImpl(); + break; + case FLOAT_TYPE: + checkAvailable(4); + result = readFloatImpl(); + break; + case DOUBLE_TYPE: + checkAvailable(8); + result = readDoubleImpl(); + break; + case NULL_STRING_TYPE: + result = null; + break; + case STRING_TYPE: + checkAvailable(1); + result = readStringImpl(); + break; + } + return result; + } + catch (RuntimeException e) + { + _data.position(position); + throw e; + } + } + + protected void writeBoolean(boolean b) throws JMSException + { + writeTypeDiscriminator(BOOLEAN_TYPE); + _data.put(b ? (byte) 1 : (byte) 0); + } + + protected void writeByte(byte b) throws JMSException + { + writeTypeDiscriminator(BYTE_TYPE); + _data.put(b); + } + + protected void writeShort(short i) throws JMSException + { + writeTypeDiscriminator(SHORT_TYPE); + _data.putShort(i); + } + + protected void writeChar(char c) throws JMSException + { + writeTypeDiscriminator(CHAR_TYPE); + _data.putChar(c); + } + + protected void writeInt(int i) throws JMSException + { + writeTypeDiscriminator(INT_TYPE); + writeIntImpl(i); + } + + protected void writeIntImpl(int i) + { + _data.putInt(i); + } + + protected void writeLong(long l) throws JMSException + { + writeTypeDiscriminator(LONG_TYPE); + _data.putLong(l); + } + + protected void writeFloat(float v) throws JMSException + { + writeTypeDiscriminator(FLOAT_TYPE); + _data.putFloat(v); + } + + protected void writeDouble(double v) throws JMSException + { + writeTypeDiscriminator(DOUBLE_TYPE); + _data.putDouble(v); + } + + protected void writeString(String string) throws JMSException + { + if (string == null) + { + writeTypeDiscriminator(NULL_STRING_TYPE); + } + else + { + writeTypeDiscriminator(STRING_TYPE); + try + { + writeStringImpl(string); + } + catch (CharacterCodingException e) + { + JMSException ex = new JMSException("Unable to encode string: " + e); + ex.setLinkedException(e); + throw ex; + } + } + } + + protected void writeStringImpl(String string) + throws CharacterCodingException + { + _data.putString(string, Charset.forName("UTF-8").newEncoder()); + // we must write the null terminator ourselves + _data.put((byte) 0); + } + + protected void writeBytes(byte[] bytes) throws JMSException + { + writeBytes(bytes, 0, bytes == null ? 0 : bytes.length); + } + + protected void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + writeTypeDiscriminator(BYTEARRAY_TYPE); + if (bytes == null) + { + _data.putInt(-1); + } + else + { + _data.putInt(length); + _data.put(bytes, offset, length); + } + } + + protected void writeObject(Object object) throws JMSException + { + checkWritable(); + Class clazz; + + if (object == null) + { + // string handles the output of null values + clazz = String.class; + } + else + { + clazz = object.getClass(); + } + + if (clazz == Byte.class) + { + writeByte((Byte) object); + } + else if (clazz == Boolean.class) + { + writeBoolean((Boolean) object); + } + else if (clazz == byte[].class) + { + writeBytes((byte[]) object); + } + else if (clazz == Short.class) + { + writeShort((Short) object); + } + else if (clazz == Character.class) + { + writeChar((Character) object); + } + else if (clazz == Integer.class) + { + writeInt((Integer) object); + } + else if (clazz == Long.class) + { + writeLong((Long) object); + } + else if (clazz == Float.class) + { + writeFloat((Float) object); + } + else if (clazz == Double.class) + { + writeDouble((Double) object); + } + else if (clazz == String.class) + { + writeString((String) object); + } + else + { + throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java new file mode 100644 index 0000000000..0700ce5d23 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -0,0 +1,534 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.UUID; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public abstract class AbstractJMSMessage implements org.apache.qpid.jms.Message +{ + + + + protected ByteBuffer _data; + protected boolean _readableMessage = false; + protected boolean _changedData = true; + + /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ + + + + + private AMQMessageDelegate _delegate; + private boolean _redelivered; + + protected AbstractJMSMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + _delegate = delegateFactory.createDelegate(); + _data = data; + if (_data != null) + { + _data.acquire(); + } + + + _readableMessage = (data != null); + _changedData = (data == null); + + } + + protected AbstractJMSMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + + _delegate = delegate; + + _data = data; + if (_data != null) + { + _data.acquire(); + } + + _readableMessage = data != null; + + } + + public String getJMSMessageID() throws JMSException + { + return _delegate.getJMSMessageID(); + } + + public void setJMSMessageID(String messageId) throws JMSException + { + _delegate.setJMSMessageID(messageId); + } + + public void setJMSMessageID(UUID messageId) throws JMSException + { + _delegate.setJMSMessageID(messageId); + } + + + public long getJMSTimestamp() throws JMSException + { + return _delegate.getJMSTimestamp(); + } + + public void setJMSTimestamp(long timestamp) throws JMSException + { + _delegate.setJMSTimestamp(timestamp); + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException + { + return _delegate.getJMSCorrelationIDAsBytes(); + } + + public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException + { + _delegate.setJMSCorrelationIDAsBytes(bytes); + } + + public void setJMSCorrelationID(String correlationId) throws JMSException + { + _delegate.setJMSCorrelationID(correlationId); + } + + public String getJMSCorrelationID() throws JMSException + { + return _delegate.getJMSCorrelationID(); + } + + public Destination getJMSReplyTo() throws JMSException + { + return _delegate.getJMSReplyTo(); + } + + public void setJMSReplyTo(Destination destination) throws JMSException + { + _delegate.setJMSReplyTo(destination); + } + + public Destination getJMSDestination() throws JMSException + { + return _delegate.getJMSDestination(); + } + + public void setJMSDestination(Destination destination) + { + _delegate.setJMSDestination(destination); + } + + public int getJMSDeliveryMode() throws JMSException + { + return _delegate.getJMSDeliveryMode(); + } + + public void setJMSDeliveryMode(int i) throws JMSException + { + _delegate.setJMSDeliveryMode(i); + } + + + public boolean getJMSRedelivered() throws JMSException + { + return _redelivered; + } + + public void setJMSRedelivered(boolean b) throws JMSException + { + _redelivered = b; + } + + + public String getJMSType() throws JMSException + { + return _delegate.getJMSType(); + } + + public void setJMSType(String string) throws JMSException + { + _delegate.setJMSType(string); + } + + public long getJMSExpiration() throws JMSException + { + return _delegate.getJMSExpiration(); + } + + public void setJMSExpiration(long l) throws JMSException + { + _delegate.setJMSExpiration(l); + } + + public int getJMSPriority() throws JMSException + { + return _delegate.getJMSPriority(); + } + + public void setJMSPriority(int i) throws JMSException + { + _delegate.setJMSPriority(i); + } + + + public boolean propertyExists(String propertyName) throws JMSException + { + return _delegate.propertyExists(propertyName); + } + + public boolean getBooleanProperty(final String s) + throws JMSException + { + return _delegate.getBooleanProperty(s); + } + + public byte getByteProperty(final String s) + throws JMSException + { + return _delegate.getByteProperty(s); + } + + public short getShortProperty(final String s) + throws JMSException + { + return _delegate.getShortProperty(s); + } + + public int getIntProperty(final String s) + throws JMSException + { + return _delegate.getIntProperty(s); + } + + public long getLongProperty(final String s) + throws JMSException + { + return _delegate.getLongProperty(s); + } + + public float getFloatProperty(final String s) + throws JMSException + { + return _delegate.getFloatProperty(s); + } + + public double getDoubleProperty(final String s) + throws JMSException + { + return _delegate.getDoubleProperty(s); + } + + public String getStringProperty(final String s) + throws JMSException + { + return _delegate.getStringProperty(s); + } + + public Object getObjectProperty(final String s) + throws JMSException + { + return _delegate.getObjectProperty(s); + } + + public Enumeration getPropertyNames() + throws JMSException + { + return _delegate.getPropertyNames(); + } + + public void setBooleanProperty(final String s, final boolean b) + throws JMSException + { + _delegate.setBooleanProperty(s, b); + } + + public void setByteProperty(final String s, final byte b) + throws JMSException + { + _delegate.setByteProperty(s, b); + } + + public void setShortProperty(final String s, final short i) + throws JMSException + { + _delegate.setShortProperty(s, i); + } + + public void setIntProperty(final String s, final int i) + throws JMSException + { + _delegate.setIntProperty(s, i); + } + + public void setLongProperty(final String s, final long l) + throws JMSException + { + _delegate.setLongProperty(s, l); + } + + public void setFloatProperty(final String s, final float v) + throws JMSException + { + _delegate.setFloatProperty(s, v); + } + + public void setDoubleProperty(final String s, final double v) + throws JMSException + { + _delegate.setDoubleProperty(s, v); + } + + public void setStringProperty(final String s, final String s1) + throws JMSException + { + _delegate.setStringProperty(s, s1); + } + + public void setObjectProperty(final String s, final Object o) + throws JMSException + { + _delegate.setObjectProperty(s, o); + } + + + + public void clearProperties() throws JMSException + { + _delegate.clearProperties(); + } + + public void clearBody() throws JMSException + { + clearBodyImpl(); + _readableMessage = false; + + } + + + public void acknowledgeThis() throws JMSException + { + _delegate.acknowledgeThis(); + } + + public void acknowledge() throws JMSException + { + _delegate.acknowledge(); + } + + /** + * This forces concrete classes to implement clearBody() + * + * @throws JMSException + */ + public abstract void clearBodyImpl() throws JMSException; + + /** + * Get a String representation of the body of the message. Used in the toString() method which outputs this before + * message properties. + */ + public abstract String toBodyString() throws JMSException; + + protected abstract String getMimeType(); + + + + public String toString() + { + try + { + StringBuffer buf = new StringBuffer("Body:\n"); + buf.append(toBodyString()); + buf.append("\nJMS Correlation ID: ").append(getJMSCorrelationID()); + buf.append("\nJMS timestamp: ").append(getJMSTimestamp()); + buf.append("\nJMS expiration: ").append(getJMSExpiration()); + buf.append("\nJMS priority: ").append(getJMSPriority()); + buf.append("\nJMS delivery mode: ").append(getJMSDeliveryMode()); + //buf.append("\nJMS reply to: ").append(String.valueOf(getJMSReplyTo())); + buf.append("\nJMS Redelivered: ").append(_redelivered); + buf.append("\nJMS Destination: ").append(getJMSDestination()); + buf.append("\nJMS Type: ").append(getJMSType()); + buf.append("\nJMS MessageID: ").append(getJMSMessageID()); + buf.append("\nAMQ message number: ").append(getDeliveryTag()); + + buf.append("\nProperties:"); + final Enumeration propertyNames = getPropertyNames(); + if (!propertyNames.hasMoreElements()) + { + buf.append(""); + } + else + { + buf.append('\n'); + while(propertyNames.hasMoreElements()) + { + String propertyName = (String) propertyNames.nextElement(); + buf.append(propertyName).append(":\t").append(getObjectProperty(propertyName)); + } + + } + + return buf.toString(); + } + catch (JMSException e) + { + return e.toString(); + } + } + + + public AMQMessageDelegate getDelegate() + { + return _delegate; + } + + public ByteBuffer getData() + { + // make sure we rewind the data just in case any method has moved the + // position beyond the start + if (_data != null) + { + reset(); + } + + return _data; + } + + protected void checkReadable() throws MessageNotReadableException + { + if (!_readableMessage) + { + throw new MessageNotReadableException("You need to call reset() to make the message readable"); + } + } + + protected void checkWritable() throws MessageNotWriteableException + { + if (_readableMessage) + { + throw new MessageNotWriteableException("You need to call clearBody() to make the message writable"); + } + } + + public void reset() + { + if (!_changedData) + { + _data.rewind(); + } + else + { + _data.flip(); + _changedData = false; + } + } + + public int getContentLength() + { + if(_data != null) + { + return _data.remaining(); + } + else + { + return 0; + } + } + + public void receivedFromServer() + { + _changedData = false; + } + + /** + * The session is set when CLIENT_ACKNOWLEDGE mode is used so that the CHANNEL ACK can be sent when the user calls + * acknowledge() + * + * @param s the AMQ session that delivered this message + */ + public void setAMQSession(AMQSession s) + { + _delegate.setAMQSession(s); + } + + public AMQSession getAMQSession() + { + return _delegate.getAMQSession(); + } + + /** + * Get the AMQ message number assigned to this message + * + * @return the message number + */ + public long getDeliveryTag() + { + return _delegate.getDeliveryTag(); + } + + /** Invoked prior to sending the message. Allows the message to be modified if necessary before sending. */ + public void prepareForSending() throws JMSException + { + } + + + public void setContentType(String contentType) + { + _delegate.setContentType(contentType); + } + + public String getContentType() + { + return _delegate.getContentType(); + } + + public void setEncoding(String encoding) + { + _delegate.setEncoding(encoding); + } + + public String getEncoding() + { + return _delegate.getEncoding(); + } + + public String getReplyToString() + { + return _delegate.getReplyToString(); + } + + protected void removeProperty(final String propertyName) throws JMSException + { + _delegate.removeProperty(propertyName); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java new file mode 100644 index 0000000000..3071abf6ff --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java @@ -0,0 +1,187 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.transport.Struct; +import org.apache.qpid.transport.MessageProperties; +import org.apache.qpid.transport.DeliveryProperties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; + +import java.util.Iterator; +import java.util.List; + +public abstract class AbstractJMSMessageFactory implements MessageFactory +{ + private static final Logger _logger = LoggerFactory.getLogger(AbstractJMSMessageFactory.class); + + protected AbstractJMSMessage create08MessageWithBody(long messageNbr, ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, + List bodies) throws AMQException + { + ByteBuffer data; + final boolean debug = _logger.isDebugEnabled(); + + // we optimise the non-fragmented case to avoid copying + if ((bodies != null) && (bodies.size() == 1)) + { + if (debug) + { + _logger.debug("Non-fragmented message body (bodySize=" + contentHeader.bodySize + ")"); + } + + data = ((ContentBody) bodies.get(0)).payload; + } + else if (bodies != null) + { + if (debug) + { + _logger.debug("Fragmented message body (" + bodies + .size() + " frames, bodySize=" + contentHeader.bodySize + ")"); + } + + data = ByteBuffer.allocate((int) contentHeader.bodySize); // XXX: Is cast a problem? + final Iterator it = bodies.iterator(); + while (it.hasNext()) + { + ContentBody cb = (ContentBody) it.next(); + final ByteBuffer payload = cb.payload; + if(payload.isDirect() || payload.isReadOnly()) + { + data.put(payload); + } + else + { + data.put(payload.array(), payload.arrayOffset(), payload.limit()); + } + + payload.release(); + } + + data.flip(); + } + else // bodies == null + { + data = ByteBuffer.allocate(0); + } + + if (debug) + { + _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining=" + data + .remaining()); + } + + AMQMessageDelegate delegate = new AMQMessageDelegate_0_8(messageNbr, + (BasicContentHeaderProperties) contentHeader.properties, + exchange, routingKey); + + return createMessage(delegate, data); + } + + protected abstract AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException; + + + protected AbstractJMSMessage create010MessageWithBody(long messageNbr, Struct[] contentHeader, + java.nio.ByteBuffer body) throws AMQException + { + ByteBuffer data; + final boolean debug = _logger.isDebugEnabled(); + + + if (body != null) + { + data = ByteBuffer.wrap(body); + } + else // body == null + { + data = ByteBuffer.allocate(0); + } + + if (debug) + { + _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining=" + data + .remaining()); + } + // set the properties of this message + MessageProperties mprop; + DeliveryProperties devprop; + if( contentHeader.length >1 ) + { + mprop = (MessageProperties) contentHeader[0]; + devprop = (DeliveryProperties) contentHeader[1]; + } + else + { + mprop = new MessageProperties(); + devprop = (DeliveryProperties) contentHeader[0]; + } + + AMQMessageDelegate delegate = new AMQMessageDelegate_0_10(mprop, devprop, messageNbr); + + AbstractJMSMessage message = createMessage(delegate, data); + return message; + } + + private static final String asString(byte[] bytes) + { + if (bytes == null) + { + return null; + } + else + { + return new String(bytes); + } + } + + + public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, List bodies) + throws JMSException, AMQException + { + final AbstractJMSMessage msg = create08MessageWithBody(messageNbr, contentHeader, exchange, routingKey, bodies); + msg.setJMSRedelivered(redelivered); + msg.receivedFromServer(); + return msg; + } + + public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, Struct[] contentHeader, + java.nio.ByteBuffer body) + throws JMSException, AMQException + { + final AbstractJMSMessage msg = + create010MessageWithBody(messageNbr, contentHeader, body); + msg.setJMSRedelivered(redelivered); + msg.receivedFromServer(); + return msg; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/CloseConsumerMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/CloseConsumerMessage.java new file mode 100644 index 0000000000..4af04912e5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/CloseConsumerMessage.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import org.apache.qpid.client.BasicMessageConsumer; + +public final class CloseConsumerMessage extends UnprocessedMessage +{ + + public CloseConsumerMessage(BasicMessageConsumer consumer) + { + super(consumer.getConsumerTag()); + } + + + public long getDeliveryTag() + { + return 0; + } + + public boolean isRedelivered() + { + return false; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java new file mode 100644 index 0000000000..5b8a4ce878 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/FiledTableSupport.java @@ -0,0 +1,56 @@ +package org.apache.qpid.client.message; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + +public class FiledTableSupport +{ + public static FieldTable convertToFieldTable(Map props) + { + FieldTable ft = new FieldTable(); + if (props != null) + { + for (String key : props.keySet()) + { + ft.setObject(key, props.get(key)); + } + } + return ft; + } + + public static Map convertToMap(FieldTable ft) + { + Map map = new HashMap(); + for (AMQShortString key: ft.keySet() ) + { + map.put(key.asString(), ft.getObject(key)); + } + + return map; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java new file mode 100644 index 0000000000..cd9d7ccf8b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java @@ -0,0 +1,390 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessage +{ + public static final String MIME_TYPE = "application/octet-stream"; + + + + public JMSBytesMessage(AMQMessageDelegateFactory delegateFactory) + { + this(delegateFactory,null); + + } + + /** + * Construct a bytes message with existing data. + * + * @param delegateFactory + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + */ + JMSBytesMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + + super(delegateFactory, data); // this instanties a content header + } + + JMSBytesMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + super(delegate, data); + } + + + public void reset() + { + super.reset(); + _readableMessage = true; + } + + protected String getMimeType() + { + return MIME_TYPE; + } + + public long getBodyLength() throws JMSException + { + checkReadable(); + return _data.limit(); + } + + public boolean readBoolean() throws JMSException + { + checkReadable(); + checkAvailable(1); + return _data.get() != 0; + } + + public byte readByte() throws JMSException + { + checkReadable(); + checkAvailable(1); + return _data.get(); + } + + public int readUnsignedByte() throws JMSException + { + checkReadable(); + checkAvailable(1); + return _data.getUnsigned(); + } + + public short readShort() throws JMSException + { + checkReadable(); + checkAvailable(2); + return _data.getShort(); + } + + public int readUnsignedShort() throws JMSException + { + checkReadable(); + checkAvailable(2); + return _data.getUnsignedShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws JMSException + */ + public char readChar() throws JMSException + { + checkReadable(); + checkAvailable(2); + return _data.getChar(); + } + + public int readInt() throws JMSException + { + checkReadable(); + checkAvailable(4); + return _data.getInt(); + } + + public long readLong() throws JMSException + { + checkReadable(); + checkAvailable(8); + return _data.getLong(); + } + + public float readFloat() throws JMSException + { + checkReadable(); + checkAvailable(4); + return _data.getFloat(); + } + + public double readDouble() throws JMSException + { + checkReadable(); + checkAvailable(8); + return _data.getDouble(); + } + + public String readUTF() throws JMSException + { + checkReadable(); + // we check only for one byte since theoretically the string could be only a + // single byte when using UTF-8 encoding + + try + { + short length = readShort(); + if(length == 0) + { + return ""; + } + else + { + CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder(); + ByteBuffer encodedString = _data.slice(); + encodedString.limit(length); + _data.position(_data.position()+length); + CharBuffer string = decoder.decode(encodedString.buf()); + + return string.toString(); + } + + + + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Error decoding byte stream as a UTF8 string: " + e); + je.setLinkedException(e); + throw je; + } + } + + public int readBytes(byte[] bytes) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + checkReadable(); + int count = (_data.remaining() >= bytes.length ? bytes.length : _data.remaining()); + if (count == 0) + { + return -1; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + public int readBytes(byte[] bytes, int maxLength) throws JMSException + { + if (bytes == null) + { + throw new IllegalArgumentException("byte array must not be null"); + } + if (maxLength > bytes.length) + { + throw new IllegalArgumentException("maxLength must be <= bytes.length"); + } + checkReadable(); + int count = (_data.remaining() >= maxLength ? maxLength : _data.remaining()); + if (count == 0) + { + return -1; + } + else + { + _data.get(bytes, 0, count); + return count; + } + } + + public void writeBoolean(boolean b) throws JMSException + { + checkWritable(); + _changedData = true; + _data.put(b ? (byte) 1 : (byte) 0); + } + + public void writeByte(byte b) throws JMSException + { + checkWritable(); + _changedData = true; + _data.put(b); + } + + public void writeShort(short i) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putShort(i); + } + + public void writeChar(char c) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putChar(c); + } + + public void writeInt(int i) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putInt(i); + } + + public void writeLong(long l) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putLong(l); + } + + public void writeFloat(float v) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putFloat(v); + } + + public void writeDouble(double v) throws JMSException + { + checkWritable(); + _changedData = true; + _data.putDouble(v); + } + + public void writeUTF(String string) throws JMSException + { + checkWritable(); + try + { + CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder(); + java.nio.ByteBuffer encodedString = encoder.encode(CharBuffer.wrap(string)); + + _data.putShort((short)encodedString.limit()); + _data.put(encodedString); + _changedData = true; + //_data.putString(string, Charset.forName("UTF-8").newEncoder()); + // we must add the null terminator manually + //_data.put((byte)0); + } + catch (CharacterCodingException e) + { + JMSException ex = new JMSException("Unable to encode string: " + e); + ex.setLinkedException(e); + throw ex; + } + } + + public void writeBytes(byte[] bytes) throws JMSException + { + checkWritable(); + _data.put(bytes); + _changedData = true; + } + + public void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + checkWritable(); + _data.put(bytes, offset, length); + _changedData = true; + } + + public void writeObject(Object object) throws JMSException + { + checkWritable(); + if (object == null) + { + throw new NullPointerException("Argument must not be null"); + } + Class clazz = object.getClass(); + if (clazz == Byte.class) + { + writeByte((Byte) object); + } + else if (clazz == Boolean.class) + { + writeBoolean((Boolean) object); + } + else if (clazz == byte[].class) + { + writeBytes((byte[]) object); + } + else if (clazz == Short.class) + { + writeShort((Short) object); + } + else if (clazz == Character.class) + { + writeChar((Character) object); + } + else if (clazz == Integer.class) + { + writeInt((Integer) object); + } + else if (clazz == Long.class) + { + writeLong((Long) object); + } + else if (clazz == Float.class) + { + writeFloat((Float) object); + } + else if (clazz == Double.class) + { + writeDouble((Double) object); + } + else if (clazz == String.class) + { + writeUTF((String) object); + } + else + { + throw new MessageFormatException("Only primitives plus byte arrays and String are valid types"); + } + } + + public String toString() + { + return String.valueOf(System.identityHashCode(this)); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java new file mode 100644 index 0000000000..cb04ebee1b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSBytesMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + return new JMSBytesMessage(delegate, data); + } + + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + return new JMSBytesMessage(delegateFactory); + } + + // 0_10 specific + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java new file mode 100644 index 0000000000..6215652c80 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java @@ -0,0 +1,552 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.util.Enumeration; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQPInvalidClassException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; + + +public final class JMSHeaderAdapter +{ + private final FieldTable _headers; + + public JMSHeaderAdapter(FieldTable headers) + { + _headers = headers; + } + + + public FieldTable getHeaders() + { + return _headers; + } + + public boolean getBoolean(String string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getBoolean can't use " + string + " item."); + } + else + { + return Boolean.valueOf((String) str); + } + } + else + { + b = Boolean.valueOf(null); + } + } + + return b; + } + + public boolean getBoolean(AMQShortString string) throws JMSException + { + checkPropertyName(string); + Boolean b = getHeaders().getBoolean(string); + + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getBoolean can't use " + string + " item."); + } + else + { + return Boolean.valueOf((String) str); + } + } + else + { + b = Boolean.valueOf(null); + } + } + + return b; + } + + public char getCharacter(String string) throws JMSException + { + checkPropertyName(string); + Character c = getHeaders().getCharacter(string); + + if (c == null) + { + if (getHeaders().isNullStringValue(string)) + { + throw new NullPointerException("Cannot convert null char"); + } + else + { + throw new MessageFormatException("getChar can't use " + string + " item."); + } + } + else + { + return (char) c; + } + } + + public byte[] getBytes(String string) throws JMSException + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) throws JMSException + { + checkPropertyName(string); + + byte[] bs = getHeaders().getBytes(string); + + if (bs == null) + { + throw new MessageFormatException("getBytes can't use " + string + " item."); + } + else + { + return bs; + } + } + + public byte getByte(String string) throws JMSException + { + checkPropertyName(string); + Byte b = getHeaders().getByte(string); + if (b == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getByte can't use " + string + " item."); + } + else + { + return Byte.valueOf((String) str); + } + } + else + { + b = Byte.valueOf(null); + } + } + + return b; + } + + public short getShort(String string) throws JMSException + { + checkPropertyName(string); + Short s = getHeaders().getShort(string); + + if (s == null) + { + s = Short.valueOf(getByte(string)); + } + + return s; + } + + public int getInteger(String string) throws JMSException + { + checkPropertyName(string); + Integer i = getHeaders().getInteger(string); + + if (i == null) + { + i = Integer.valueOf(getShort(string)); + } + + return i; + } + + public long getLong(String string) throws JMSException + { + checkPropertyName(string); + Long l = getHeaders().getLong(string); + + if (l == null) + { + l = Long.valueOf(getInteger(string)); + } + + return l; + } + + public float getFloat(String string) throws JMSException + { + checkPropertyName(string); + Float f = getHeaders().getFloat(string); + + if (f == null) + { + if (getHeaders().containsKey(string)) + { + Object str = getHeaders().getObject(string); + + if (str == null || !(str instanceof String)) + { + throw new MessageFormatException("getFloat can't use " + string + " item."); + } + else + { + return Float.valueOf((String) str); + } + } + else + { + f = Float.valueOf(null); + } + + } + + return f; + } + + public double getDouble(String string) throws JMSException + { + checkPropertyName(string); + Double d = getHeaders().getDouble(string); + + if (d == null) + { + d = Double.valueOf(getFloat(string)); + } + + return d; + } + + public String getString(String string) throws JMSException + { + checkPropertyName(string); + String s = getHeaders().getString(string); + + if (s == null) + { + if (getHeaders().containsKey(string)) + { + Object o = getHeaders().getObject(string); + if (o instanceof byte[]) + { + throw new MessageFormatException("getObject couldn't find " + string + " item."); + } + else + { + if (o == null) + { + return null; + } + else + { + s = String.valueOf(o); + } + } + }//else return s // null; + } + + return s; + } + + public Object getObject(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().getObject(string); + } + + public void setBoolean(AMQShortString string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setBoolean(String string, boolean b) throws JMSException + { + checkPropertyName(string); + getHeaders().setBoolean(string, b); + } + + public void setChar(String string, char c) throws JMSException + { + checkPropertyName(string); + getHeaders().setChar(string, c); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + checkPropertyName(string); + return getHeaders().setBytes(string, bytes, start, length); + } + + public void setByte(String string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + public void setByte(AMQShortString string, byte b) throws JMSException + { + checkPropertyName(string); + getHeaders().setByte(string, b); + } + + + public void setShort(String string, short i) throws JMSException + { + checkPropertyName(string); + getHeaders().setShort(string, i); + } + + public void setInteger(String string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setInteger(AMQShortString string, int i) throws JMSException + { + checkPropertyName(string); + getHeaders().setInteger(string, i); + } + + public void setLong(String string, long l) throws JMSException + { + checkPropertyName(string); + getHeaders().setLong(string, l); + } + + public void setFloat(String string, float v) throws JMSException + { + checkPropertyName(string); + getHeaders().setFloat(string, v); + } + + public void setDouble(String string, double v) throws JMSException + { + checkPropertyName(string); + getHeaders().setDouble(string, v); + } + + public void setString(String string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setString(AMQShortString string, String string1) throws JMSException + { + checkPropertyName(string); + getHeaders().setString(string, string1); + } + + public void setObject(String string, Object object) throws JMSException + { + checkPropertyName(string); + try + { + getHeaders().setObject(string, object); + } + catch (AMQPInvalidClassException aice) + { + MessageFormatException mfe = new MessageFormatException("Only primatives are allowed object is:" + object.getClass()); + mfe.setLinkedException(aice); + throw mfe; + } + } + + public boolean itemExists(String string) throws JMSException + { + checkPropertyName(string); + return getHeaders().containsKey(string); + } + + public Enumeration getPropertyNames() + { + return getHeaders().getPropertyNames(); + } + + public void clear() + { + getHeaders().clear(); + } + + public boolean propertyExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().propertyExists(propertyName); + } + + public Object put(Object key, Object value) + { + checkPropertyName(key.toString()); + return getHeaders().setObject(key.toString(), value); + } + + public Object remove(AMQShortString propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public Object remove(String propertyName) + { + checkPropertyName(propertyName); + return getHeaders().remove(propertyName); + } + + public boolean isEmpty() + { + return getHeaders().isEmpty(); + } + + public void writeToBuffer(ByteBuffer data) + { + getHeaders().writeToBuffer(data); + } + + public Enumeration getMapNames() + { + return getPropertyNames(); + } + + protected void checkPropertyName(CharSequence propertyName) + { + if (propertyName == null) + { + throw new IllegalArgumentException("Property name must not be null"); + } + else if (propertyName.length() == 0) + { + throw new IllegalArgumentException("Property name must not be the empty string"); + } + + checkIdentiferFormat(propertyName); + } + + protected void checkIdentiferFormat(CharSequence propertyName) + { +// JMS requirements 3.5.1 Property Names +// Identifiers: +// - An identifier is an unlimited-length character sequence that must begin +// with a Java identifier start character; all following characters must be Java +// identifier part characters. An identifier start character is any character for +// which the method Character.isJavaIdentifierStart returns true. This includes +// '_' and '$'. An identifier part character is any character for which the +// method Character.isJavaIdentifierPart returns true. +// - Identifiers cannot be the names NULL, TRUE, or FALSE. +// Identifiers cannot be NOT, AND, OR, BETWEEN, LIKE, IN, IS, or +// ESCAPE. +// Identifiers are either header field references or property references. The +// type of a property value in a message selector corresponds to the type +// used to set the property. If a property that does not exist in a message is +// referenced, its value is NULL. The semantics of evaluating NULL values +// in a selector are described in Section 3.8.1.2, Null Values. +// The conversions that apply to the get methods for properties do not +// apply when a property is used in a message selector expression. For +// example, suppose you set a property as a string value, as in the +// following: +// myMessage.setStringProperty("NumberOfOrders", "2"); +// The following expression in a message selector would evaluate to false, +// because a string cannot be used in an arithmetic expression: +// "NumberOfOrders > 1" +// Identifiers are case sensitive. +// Message header field references are restricted to JMSDeliveryMode, +// JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and +// JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be +// null and if so are treated as a NULL value. + + if (Boolean.getBoolean("strict-jms")) + { + // JMS start character + if (!(Character.isJavaIdentifierStart(propertyName.charAt(0)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' does not start with a valid JMS identifier start character"); + } + + // JMS part character + int length = propertyName.length(); + for (int c = 1; c < length; c++) + { + if (!(Character.isJavaIdentifierPart(propertyName.charAt(c)))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' contains an invalid JMS identifier character"); + } + } + + // JMS invalid names + if ((propertyName.equals("NULL") + || propertyName.equals("TRUE") + || propertyName.equals("FALSE") + || propertyName.equals("NOT") + || propertyName.equals("AND") + || propertyName.equals("OR") + || propertyName.equals("BETWEEN") + || propertyName.equals("LIKE") + || propertyName.equals("IN") + || propertyName.equals("IS") + || propertyName.equals("ESCAPE"))) + { + throw new IllegalArgumentException("Identifier '" + propertyName + "' is not allowed in JMS"); + } + } + + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java new file mode 100644 index 0000000000..b6e013ac8f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.client.message; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import java.nio.charset.CharacterCodingException; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jms.MapMessage +{ + private static final Logger _logger = LoggerFactory.getLogger(JMSMapMessage.class); + + public static final String MIME_TYPE = "jms/map-message"; + + + private Map _map = new HashMap(); + + public JMSMapMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + this(delegateFactory, null); + } + + JMSMapMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) throws JMSException + { + + super(delegateFactory, data); // this instantiates a content header + if(data != null) + { + populateMapFromData(); + } + + } + + JMSMapMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + + super(delegate, data); + try + { + populateMapFromData(); + } + catch (JMSException je) + { + throw new AMQException(null, "Error populating MapMessage from ByteBuffer", je); + + } + + } + + + public String toBodyString() throws JMSException + { + return _map == null ? "" : _map.toString(); + } + + protected String getMimeType() + { + return MIME_TYPE; + } + + public ByteBuffer getData() + { + // What if _data is null? + writeMapToData(); + + return super.getData(); + } + + @Override + public void clearBodyImpl() throws JMSException + { + super.clearBodyImpl(); + _map.clear(); + } + + public boolean getBoolean(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Boolean) + { + return ((Boolean) value).booleanValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Boolean.valueOf((String) value); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to boolean."); + } + + } + + public byte getByte(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Byte) + { + return ((Byte) value).byteValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Byte.valueOf((String) value).byteValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to byte."); + } + } + + public short getShort(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Short) + { + return ((Short) value).shortValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).shortValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Short.valueOf((String) value).shortValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to short."); + } + + } + + public int getInt(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Integer) + { + return ((Integer) value).intValue(); + } + else if (value instanceof Short) + { + return ((Short) value).intValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).intValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Integer.valueOf((String) value).intValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to int."); + } + + } + + public long getLong(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Long) + { + return ((Long) value).longValue(); + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + + if (value instanceof Short) + { + return ((Short) value).longValue(); + } + + if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value).longValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to long."); + } + + } + + public char getChar(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (!_map.containsKey(propName)) + { + throw new MessageFormatException("Property " + propName + " not present"); + } + else if (value instanceof Character) + { + return ((Character) value).charValue(); + } + else if (value == null) + { + throw new NullPointerException("Property " + propName + " has null value and therefore cannot " + + "be converted to char."); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to boolan."); + } + + } + + public float getFloat(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Float) + { + return ((Float) value).floatValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Float.valueOf((String) value).floatValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to float."); + } + } + + public double getDouble(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (value instanceof Double) + { + return ((Double) value).doubleValue(); + } + else if (value instanceof Float) + { + return ((Float) value).doubleValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Double.valueOf((String) value).doubleValue(); + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to double."); + } + } + + public String getString(String propName) throws JMSException + { + Object value = _map.get(propName); + + if ((value instanceof String) || (value == null)) + { + return (String) value; + } + else if (value instanceof byte[]) + { + throw new MessageFormatException("Property " + propName + " of type byte[] " + "cannot be converted to String."); + } + else + { + return value.toString(); + } + + } + + public byte[] getBytes(String propName) throws JMSException + { + Object value = _map.get(propName); + + if (!_map.containsKey(propName)) + { + throw new MessageFormatException("Property " + propName + " not present"); + } + else if ((value instanceof byte[]) || (value == null)) + { + return (byte[]) value; + } + else + { + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to byte[]."); + } + } + + public Object getObject(String propName) throws JMSException + { + return _map.get(propName); + } + + public Enumeration getMapNames() throws JMSException + { + return Collections.enumeration(_map.keySet()); + } + + public void setBoolean(String propName, boolean b) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, b); + } + + public void setByte(String propName, byte b) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, b); + } + + public void setShort(String propName, short i) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, i); + } + + public void setChar(String propName, char c) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, c); + } + + public void setInt(String propName, int i) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, i); + } + + public void setLong(String propName, long l) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, l); + } + + public void setFloat(String propName, float v) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, v); + } + + public void setDouble(String propName, double v) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, v); + } + + public void setString(String propName, String string1) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, string1); + } + + public void setBytes(String propName, byte[] bytes) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + _map.put(propName, bytes); + } + + public void setBytes(String propName, byte[] bytes, int offset, int length) throws JMSException + { + if ((offset == 0) && (length == bytes.length)) + { + setBytes(propName, bytes); + } + else + { + byte[] newBytes = new byte[length]; + System.arraycopy(bytes, offset, newBytes, 0, length); + setBytes(propName, newBytes); + } + } + + public void setObject(String propName, Object value) throws JMSException + { + checkWritable(); + checkPropertyName(propName); + if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) + || (value instanceof Long) || (value instanceof Character) || (value instanceof Float) + || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) || (value == null)) + { + _map.put(propName, value); + } + else + { + throw new MessageFormatException("Cannot set property " + propName + " to value " + value + "of type " + + value.getClass().getName() + "."); + } + } + + private void checkPropertyName(String propName) + { + if ((propName == null) || propName.equals("")) + { + throw new IllegalArgumentException("Property name cannot be null, or the empty String."); + } + } + + public boolean itemExists(String propName) throws JMSException + { + return _map.containsKey(propName); + } + + private void populateMapFromData() throws JMSException + { + if (_data != null) + { + _data.rewind(); + + final int entries = readIntImpl(); + for (int i = 0; i < entries; i++) + { + String propName = readStringImpl(); + Object value = readObject(); + _map.put(propName, value); + } + } + else + { + _map.clear(); + } + } + + private void writeMapToData() + { + allocateInitialBuffer(); + final int size = _map.size(); + writeIntImpl(size); + for (Map.Entry entry : _map.entrySet()) + { + try + { + writeStringImpl(entry.getKey()); + } + catch (CharacterCodingException e) + { + throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey(), e); + + } + + try + { + writeObject(entry.getValue()); + } + catch (JMSException e) + { + Object value = entry.getValue(); + throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey() + " value : " + value + + " (type: " + value.getClass().getName() + ").", e); + } + } + + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java new file mode 100644 index 0000000000..eccb90560b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSMapMessageFactory extends AbstractJMSMessageFactory +{ + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + return new JMSMapMessage(delegateFactory); + } + + protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + return new JMSMapMessage(delegate, data); + + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java new file mode 100644 index 0000000000..39b9597af1 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.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.client.message; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; +import javax.jms.ObjectMessage; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessage +{ + public static final String MIME_TYPE = "application/java-object-stream"; + + + private static final int DEFAULT_BUFFER_SIZE = 1024; + + /** + * Creates empty, writable message for use by producers + * @param delegateFactory + */ + public JMSObjectMessage(AMQMessageDelegateFactory delegateFactory) + { + this(delegateFactory, null); + } + + private JMSObjectMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + super(delegateFactory, data); + if (data == null) + { + _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); + _data.setAutoExpand(true); + } + + setContentType(getMimeType()); + } + + /** + * Creates read only message for delivery to consumers + */ + + JMSObjectMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + super(delegate, data); + } + + + public void clearBodyImpl() throws JMSException + { + if (_data != null) + { + _data.release(); + _data = null; + } + + + + } + + public String toBodyString() throws JMSException + { + return String.valueOf(getObject()); + } + + public String getMimeType() + { + return MIME_TYPE; + } + + public void setObject(Serializable serializable) throws JMSException + { + checkWritable(); + + if (_data == null) + { + _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); + _data.setAutoExpand(true); + } + else + { + _data.rewind(); + } + + try + { + ObjectOutputStream out = new ObjectOutputStream(_data.asOutputStream()); + out.writeObject(serializable); + out.flush(); + out.close(); + } + catch (IOException e) + { + MessageFormatException mfe = new MessageFormatException("Message not serializable: " + e); + mfe.setLinkedException(e); + throw mfe; + } + + } + + public Serializable getObject() throws JMSException + { + ObjectInputStream in = null; + if (_data == null) + { + return null; + } + + try + { + _data.rewind(); + in = new ObjectInputStream(_data.asInputStream()); + + return (Serializable) in.readObject(); + } + catch (IOException e) + { + MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e); + mfe.setLinkedException(e); + throw mfe; + } + catch (ClassNotFoundException e) + { + MessageFormatException mfe = new MessageFormatException("Could not deserialize message: " + e); + mfe.setLinkedException(e); + throw mfe; + } + finally + { + _data.rewind(); + close(in); + } + } + + private static void close(InputStream in) + { + try + { + if (in != null) + { + in.close(); + } + } + catch (IOException ignore) + { } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java new file mode 100644 index 0000000000..03851dfa01 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSObjectMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + return new JMSObjectMessage(delegate, data); + } + + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + return new JMSObjectMessage(delegateFactory); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java new file mode 100644 index 0000000000..ad2620852b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java @@ -0,0 +1,206 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; +import javax.jms.StreamMessage; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +/** + * @author Apache Software Foundation + */ +public class JMSStreamMessage extends AbstractBytesTypedMessage implements StreamMessage +{ + public static final String MIME_TYPE="jms/stream-message"; + + + + /** + * This is set when reading a byte array. The readBytes(byte[]) method supports multiple calls to read + * a byte array in multiple chunks, hence this is used to track how much is left to be read + */ + private int _byteArrayRemaining = -1; + + public JMSStreamMessage(AMQMessageDelegateFactory delegateFactory) + { + this(delegateFactory,null); + + } + + /** + * Construct a stream message with existing data. + * + * @param delegateFactory + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + */ + JMSStreamMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data) + { + + super(delegateFactory, data); // this instanties a content header + } + + JMSStreamMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + + super(delegate, data); + } + + + public void reset() + { + super.reset(); + _readableMessage = true; + } + + protected String getMimeType() + { + return MIME_TYPE; + } + + + + public boolean readBoolean() throws JMSException + { + return super.readBoolean(); + } + + + public byte readByte() throws JMSException + { + return super.readByte(); + } + + public short readShort() throws JMSException + { + return super.readShort(); + } + + /** + * Note that this method reads a unicode character as two bytes from the stream + * + * @return the character read from the stream + * @throws JMSException + */ + public char readChar() throws JMSException + { + return super.readChar(); + } + + public int readInt() throws JMSException + { + return super.readInt(); + } + + public long readLong() throws JMSException + { + return super.readLong(); + } + + public float readFloat() throws JMSException + { + return super.readFloat(); + } + + public double readDouble() throws JMSException + { + return super.readDouble(); + } + + public String readString() throws JMSException + { + return super.readString(); + } + + public int readBytes(byte[] bytes) throws JMSException + { + return super.readBytes(bytes); + } + + + public Object readObject() throws JMSException + { + return super.readObject(); + } + + public void writeBoolean(boolean b) throws JMSException + { + super.writeBoolean(b); + } + + public void writeByte(byte b) throws JMSException + { + super.writeByte(b); + } + + public void writeShort(short i) throws JMSException + { + super.writeShort(i); + } + + public void writeChar(char c) throws JMSException + { + super.writeChar(c); + } + + public void writeInt(int i) throws JMSException + { + super.writeInt(i); + } + + public void writeLong(long l) throws JMSException + { + super.writeLong(l); + } + + public void writeFloat(float v) throws JMSException + { + super.writeFloat(v); + } + + public void writeDouble(double v) throws JMSException + { + super.writeDouble(v); + } + + public void writeString(String string) throws JMSException + { + super.writeString(string); + } + + public void writeBytes(byte[] bytes) throws JMSException + { + super.writeBytes(bytes); + } + + public void writeBytes(byte[] bytes, int offset, int length) throws JMSException + { + super.writeBytes(bytes,offset,length); + } + + public void writeObject(Object object) throws JMSException + { + super.writeObject(object); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java new file mode 100644 index 0000000000..5e25db9ae0 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.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.client.message; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSStreamMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + return new JMSStreamMessage(delegate, data); + } + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + return new JMSStreamMessage(delegateFactory); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java new file mode 100644 index 0000000000..c290149cef --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.CustomJMSXProperty; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.util.Strings; + +public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.TextMessage +{ + private static final String MIME_TYPE = "text/plain"; + + private String _decodedValue; + + /** + * This constant represents the name of a property that is set when the message payload is null. + */ + private static final String PAYLOAD_NULL_PROPERTY = CustomJMSXProperty.JMS_AMQP_NULL.toString(); + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + public JMSTextMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + this(delegateFactory, null, null); + } + + JMSTextMessage(AMQMessageDelegateFactory delegateFactory, ByteBuffer data, String encoding) throws JMSException + { + super(delegateFactory, data); // this instantiates a content header + setContentType(getMimeType()); + setEncoding(encoding); + } + + JMSTextMessage(AMQMessageDelegate delegate, ByteBuffer data) + throws AMQException + { + super(delegate, data); + setContentType(getMimeType()); + _data = data; + } + + + public void clearBodyImpl() throws JMSException + { + if (_data != null) + { + _data.release(); + _data = null; + } + + _decodedValue = null; + } + + public String toBodyString() throws JMSException + { + return getText(); + } + + protected String getMimeType() + { + return MIME_TYPE; + } + + public void setText(String text) throws JMSException + { + checkWritable(); + + clearBody(); + try + { + if (text != null) + { + final String encoding = getEncoding(); + if (encoding == null || encoding.equalsIgnoreCase("UTF-8")) + { + _data = ByteBuffer.wrap(Strings.toUTF8(text)); + } + else + { + _data = ByteBuffer.wrap(text.getBytes(encoding)); + } + _data.position(_data.limit()); + _changedData=true; + } + _decodedValue = text; + } + catch (UnsupportedEncodingException e) + { + // should never occur + JMSException jmse = new JMSException("Unable to decode text data"); + jmse.setLinkedException(e); + throw jmse; + } + } + + public String getText() throws JMSException + { + if (_data == null && _decodedValue == null) + { + return null; + } + else if (_decodedValue != null) + { + return _decodedValue; + } + else + { + _data.rewind(); + + if (propertyExists(PAYLOAD_NULL_PROPERTY) && getBooleanProperty(PAYLOAD_NULL_PROPERTY)) + { + return null; + } + if (getEncoding() != null) + { + try + { + _decodedValue = _data.getString(Charset.forName(getEncoding()).newDecoder()); + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Could not decode string data: " + e); + je.setLinkedException(e); + throw je; + } + } + else + { + try + { + _decodedValue = _data.getString(DEFAULT_CHARSET.newDecoder()); + } + catch (CharacterCodingException e) + { + JMSException je = new JMSException("Could not decode string data: " + e); + je.setLinkedException(e); + throw je; + } + } + return _decodedValue; + } + } + + @Override + public void prepareForSending() throws JMSException + { + super.prepareForSending(); + if (_data == null) + { + setBooleanProperty(PAYLOAD_NULL_PROPERTY, true); + } + else + { + removeProperty(PAYLOAD_NULL_PROPERTY); + } + } + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java new file mode 100644 index 0000000000..1f4d64c78f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import javax.jms.JMSException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; + +public class JMSTextMessageFactory extends AbstractJMSMessageFactory +{ + + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException + { + return new JMSTextMessage(delegateFactory); + } + + protected AbstractJMSMessage createMessage(AMQMessageDelegate delegate, ByteBuffer data) throws AMQException + { + return new JMSTextMessage(delegate, data); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java new file mode 100644 index 0000000000..e606ef11c9 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java @@ -0,0 +1,195 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.client.AMQSession; + +import javax.jms.*; + +import java.util.Enumeration; + +public class MessageConverter +{ + + /** + * Log4J logger + */ + protected final Logger _logger = LoggerFactory.getLogger(getClass()); + + /** + * AbstractJMSMessage which will hold the converted message + */ + private AbstractJMSMessage _newMessage; + + public MessageConverter(AbstractJMSMessage message) throws JMSException + { + _newMessage = message; + } + + public MessageConverter(AMQSession session, BytesMessage bytesMessage) throws JMSException + { + bytesMessage.reset(); + + JMSBytesMessage nativeMsg = (JMSBytesMessage) session.createBytesMessage(); + + byte[] buf = new byte[1024]; + + int len; + + while ((len = bytesMessage.readBytes(buf)) != -1) + { + nativeMsg.writeBytes(buf, 0, len); + } + + _newMessage = nativeMsg; + setMessageProperties(bytesMessage); + } + + public MessageConverter(AMQSession session, MapMessage message) throws JMSException + { + MapMessage nativeMessage = session.createMapMessage(); + + Enumeration mapNames = message.getMapNames(); + while (mapNames.hasMoreElements()) + { + String name = (String) mapNames.nextElement(); + nativeMessage.setObject(name, message.getObject(name)); + } + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + } + + public MessageConverter(AMQSession session, ObjectMessage origMessage) throws JMSException + { + + ObjectMessage nativeMessage = session.createObjectMessage(); + + nativeMessage.setObject(origMessage.getObject()); + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(origMessage); + + } + + public MessageConverter(AMQSession session, TextMessage message) throws JMSException + { + TextMessage nativeMessage = session.createTextMessage(); + + nativeMessage.setText(message.getText()); + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + } + + public MessageConverter(AMQSession session, StreamMessage message) throws JMSException + { + StreamMessage nativeMessage = session.createStreamMessage(); + + try + { + message.reset(); + while (true) + { + nativeMessage.writeObject(message.readObject()); + } + } + catch (MessageEOFException e) + { + // we're at the end so don't mind the exception + } + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + } + + public MessageConverter(AMQSession session, Message message) throws JMSException + { + // Send a message with just properties. + // Throwing away content + Message nativeMessage = session.createMessage(); + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + } + + public AbstractJMSMessage getConvertedMessage() + { + return _newMessage; + } + + /** + * Sets all message properties + */ + protected void setMessageProperties(Message message) throws JMSException + { + setNonJMSProperties(message); + setJMSProperties(message); + } + + /** + * Sets all non-JMS defined properties on converted message + */ + protected void setNonJMSProperties(Message message) throws JMSException + { + Enumeration propertyNames = message.getPropertyNames(); + while (propertyNames.hasMoreElements()) + { + String propertyName = String.valueOf(propertyNames.nextElement()); + // TODO: Shouldn't need to check for JMS properties here as don't think getPropertyNames() should return them + if (!propertyName.startsWith("JMSX_")) + { + Object value = message.getObjectProperty(propertyName); + _newMessage.setObjectProperty(propertyName, value); + } + } + } + + /** + * Exposed JMS defined properties on converted message: + * JMSDestination - we don't set here + * JMSDeliveryMode - set + * JMSExpiration - we don't set here + * JMSPriority - we don't set here + * JMSMessageID - we don't set here + * JMSTimestamp - we don't set here + * JMSCorrelationID - set + * JMSReplyTo - set + * JMSType - set + * JMSRedlivered - we don't set here + */ + protected void setJMSProperties(Message message) throws JMSException + { + _newMessage.setJMSDeliveryMode(message.getJMSDeliveryMode()); + + if (message.getJMSReplyTo() != null) + { + _newMessage.setJMSReplyTo(message.getJMSReplyTo()); + } + + _newMessage.setJMSType(message.getJMSType()); + + _newMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java new file mode 100644 index 0000000000..e1275c37f7 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java @@ -0,0 +1,47 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.util.List; + +import javax.jms.JMSException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.transport.Struct; + + +public interface MessageFactory +{ + AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, + ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, + List bodies) + throws JMSException, AMQException; + + AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, + Struct[] contentHeader, + java.nio.ByteBuffer body) + throws JMSException, AMQException; + + AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory) throws JMSException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java new file mode 100644 index 0000000000..461fec73bc --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java @@ -0,0 +1,173 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.nio.ByteBuffer; + +import javax.jms.JMSException; + +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.transport.Struct; +import org.apache.qpid.transport.MessageProperties; +import org.apache.qpid.transport.MessageTransfer; +import org.apache.qpid.transport.DeliveryProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageFactoryRegistry +{ + /** + * This class logger + */ + protected final Logger _logger = LoggerFactory.getLogger(getClass()); + + private final Map _mimeStringToFactoryMap = new HashMap(); + private final Map _mimeShortStringToFactoryMap = + new HashMap(); + + /** + * Construct a new registry with the default message factories registered + * + * @return a message factory registry + */ + public static MessageFactoryRegistry newDefaultRegistry() + { + MessageFactoryRegistry mf = new MessageFactoryRegistry(); + mf.registerFactory(JMSMapMessage.MIME_TYPE, new JMSMapMessageFactory()); + mf.registerFactory("text/plain", new JMSTextMessageFactory()); + mf.registerFactory("text/xml", new JMSTextMessageFactory()); + mf.registerFactory(JMSBytesMessage.MIME_TYPE, new JMSBytesMessageFactory()); + mf.registerFactory(JMSObjectMessage.MIME_TYPE, new JMSObjectMessageFactory()); + mf.registerFactory(JMSStreamMessage.MIME_TYPE, new JMSStreamMessageFactory()); + mf.registerFactory(null, new JMSBytesMessageFactory()); + + return mf; + } + + + public void registerFactory(String mimeType, MessageFactory mf) + { + if (mf == null) + { + throw new IllegalArgumentException("Message factory must not be null"); + } + + _mimeStringToFactoryMap.put(mimeType, mf); + _mimeShortStringToFactoryMap.put(new AMQShortString(mimeType), mf); + } + + public MessageFactory deregisterFactory(String mimeType) + { + _mimeShortStringToFactoryMap.remove(new AMQShortString(mimeType)); + + return _mimeStringToFactoryMap.remove(mimeType); + } + + /** + * Create a message. This looks up the MIME type from the content header and instantiates the appropriate + * concrete message type. + * + * @param deliveryTag the AMQ message id + * @param redelivered true if redelivered + * @param contentHeader the content header that was received + * @param bodies a list of ContentBody instances @return the message. + * @throws AMQException + * @throws JMSException + */ + public AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, AMQShortString exchange, + AMQShortString routingKey, ContentHeaderBody contentHeader, List bodies) + throws AMQException, JMSException + { + BasicContentHeaderProperties properties = (BasicContentHeaderProperties) contentHeader.properties; + + // Get the message content type. This may be null for pure AMQP messages, but will always be set for JMS over + // AMQP. When the type is null, it can only be assumed that the message is a byte message. + AMQShortString contentTypeShortString = properties.getContentType(); + contentTypeShortString = (contentTypeShortString == null) ? new AMQShortString( + JMSBytesMessage.MIME_TYPE) : contentTypeShortString; + + MessageFactory mf = _mimeShortStringToFactoryMap.get(contentTypeShortString); + if (mf == null) + { + throw new AMQException(null, "Unsupport MIME type of " + properties.getContentTypeAsString(), null); + } + else + { + return mf.createMessage(deliveryTag, redelivered, contentHeader, exchange, routingKey, bodies); + } + } + + public AbstractJMSMessage createMessage(MessageTransfer transfer) throws AMQException, JMSException + { + + MessageProperties mprop = transfer.getHeader().get(MessageProperties.class); + String messageType = ""; + if ( mprop == null || mprop.getContentType() == null) + { + _logger.debug("no message type specified, building a byte message"); + messageType = JMSBytesMessage.MIME_TYPE; + } + else + { + messageType = mprop.getContentType(); + } + MessageFactory mf = _mimeStringToFactoryMap.get(messageType); + if (mf == null) + { + throw new AMQException(null, "Unsupport MIME type of " + messageType, null); + } + else + { + boolean redelivered = false; + DeliveryProperties deliverProps; + if((deliverProps = transfer.getHeader().get(DeliveryProperties.class)) != null) + { + redelivered = deliverProps.getRedelivered(); + } + return mf.createMessage(transfer.getId(), redelivered, transfer.getHeader().getStructs(), transfer.getBody()); + } + } + + + public AbstractJMSMessage createMessage(AMQMessageDelegateFactory delegateFactory, String mimeType) throws AMQException, JMSException + { + if (mimeType == null) + { + throw new IllegalArgumentException("Mime type must not be null"); + } + + MessageFactory mf = _mimeStringToFactoryMap.get(mimeType); + if (mf == null) + { + throw new AMQException(null, "Unsupport MIME type of " + mimeType, null); + } + else + { + return mf.createMessage(delegateFactory); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java new file mode 100644 index 0000000000..6e5f33a65c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/ReturnMessage.java @@ -0,0 +1,47 @@ +package org.apache.qpid.client.message; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import org.apache.qpid.framing.AMQShortString; + +public class ReturnMessage extends UnprocessedMessage_0_8 +{ + final private AMQShortString _replyText; + final private int _replyCode; + + public ReturnMessage(AMQShortString exchange, AMQShortString routingKey, AMQShortString replyText, int replyCode) + { + super(-1,0,exchange,routingKey,false); + _replyText = replyText; + _replyCode = replyCode; + } + + public int getReplyCode() + { + return _replyCode; + } + + public AMQShortString getReplyText() + { + return _replyText; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java new file mode 100644 index 0000000000..713c87260c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.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.client.message; + +import org.apache.qpid.client.BasicMessageConsumer; + + +/** + * This class contains everything needed to process a JMS message. It assembles the deliver body, the content header and + * the content body/ies. + * + * Note that the actual work of creating a JMS message for the client code's use is done outside of the MINA dispatcher + * thread in order to minimise the amount of work done in the MINA dispatcher thread. + */ +public abstract class UnprocessedMessage +{ + private final int _consumerTag; + + + public UnprocessedMessage(int consumerTag) + { + _consumerTag = consumerTag; + } + + + abstract public long getDeliveryTag(); + + + public int getConsumerTag() + { + return _consumerTag; + } + + +} \ No newline at end of file diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.java new file mode 100644 index 0000000000..f31bc88509 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_10.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.client.message; + +import org.apache.qpid.transport.MessageTransfer; + +/** + * This class contains everything needed to process a JMS message. It assembles the deliver body, the content header and + * the content body/ies. + * + * Note that the actual work of creating a JMS message for the client code's use is done outside of the MINA dispatcher + * thread in order to minimise the amount of work done in the MINA dispatcher thread. + */ +public class UnprocessedMessage_0_10 extends UnprocessedMessage +{ + private MessageTransfer _transfer; + + public UnprocessedMessage_0_10(MessageTransfer xfr) + { + super(Integer.parseInt(xfr.getDestination())); + _transfer = xfr; + } + + // additional 0_10 method + + public long getDeliveryTag() + { + return _transfer.getId(); + } + + public MessageTransfer getMessageTransfer() + { + return _transfer; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java new file mode 100644 index 0000000000..685e646d85 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage_0_8.java @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.message; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; + +/** + * This class contains everything needed to process a JMS message. It assembles the deliver body, the content header and + * the content body/ies. + * + * Note that the actual work of creating a JMS message for the client code's use is done outside of the MINA dispatcher + * thread in order to minimise the amount of work done in the MINA dispatcher thread. + */ +public class UnprocessedMessage_0_8 extends UnprocessedMessage +{ + private long _bytesReceived = 0; + + + private AMQShortString _exchange; + private AMQShortString _routingKey; + private final long _deliveryId; + protected boolean _redelivered; + + private BasicDeliverBody _deliverBody; + private ContentHeaderBody _contentHeader; + + /** List of ContentBody instances. Due to fragmentation you don't know how big this will be in general */ + private List _bodies; + + public UnprocessedMessage_0_8(long deliveryId, int consumerTag, AMQShortString exchange, AMQShortString routingKey, boolean redelivered) + { + super(consumerTag); + _exchange = exchange; + _routingKey = routingKey; + + _redelivered = redelivered; + _deliveryId = deliveryId; + } + + + public AMQShortString getExchange() + { + return _exchange; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + + public long getDeliveryTag() + { + return _deliveryId; + } + + public boolean isRedelivered() + { + return _redelivered; + } + + + public void receiveBody(ContentBody body) + { + + if (body.payload != null) + { + final long payloadSize = body.payload.remaining(); + + if (_bodies == null) + { + if (payloadSize == getContentHeader().bodySize) + { + _bodies = Collections.singletonList(body); + } + else + { + _bodies = new ArrayList(); + _bodies.add(body); + } + + } + else + { + _bodies.add(body); + } + _bytesReceived += payloadSize; + } + } + + public void setMethodBody(BasicDeliverBody deliverBody) + { + _deliverBody = deliverBody; + } + + public void setContentHeader(ContentHeaderBody contentHeader) + { + this._contentHeader = contentHeader; + } + + public boolean isAllBodyDataReceived() + { + return _bytesReceived == getContentHeader().bodySize; + } + + public BasicDeliverBody getDeliverBody() + { + return _deliverBody; + } + + public ContentHeaderBody getContentHeader() + { + return _contentHeader; + } + + public List getBodies() + { + return _bodies; + } + + public String toString() + { + StringBuilder buf = new StringBuilder(); + + if (_contentHeader != null) + { + buf.append("ContentHeader " + _contentHeader); + } + if(_deliverBody != null) + { + buf.append("Delivery tag " + _deliverBody.getDeliveryTag()); + buf.append("Consumer tag " + _deliverBody.getConsumerTag()); + buf.append("Deliver Body " + _deliverBody); + } + + return buf.toString(); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java new file mode 100644 index 0000000000..f2aca58deb --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQIoTransportProtocolSession.java @@ -0,0 +1,146 @@ +package org.apache.qpid.client.protocol; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.util.UUID; + +import javax.security.sasl.SaslClient; + +import org.apache.commons.lang.StringUtils; +import org.apache.mina.common.IdleStatus; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.ConnectionTuneParameters; +import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.transport.Sender; + +public class AMQIoTransportProtocolSession extends AMQProtocolSession +{ + + protected Sender _ioSender; + private SaslClient _saslClient; + private ConnectionTuneParameters _connectionTuneParameters; + + public AMQIoTransportProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) + { + super(protocolHandler, connection); + } + + @Override + public void closeProtocolSession(boolean waitLast) throws AMQException + { + _ioSender.close(); + _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); + } + + @Override + public void init() + { + _ioSender.send(new ProtocolInitiation(_connection.getProtocolVersion()).toNioByteBuffer()); + _ioSender.flush(); + } + + @Override + protected AMQShortString generateQueueName() + { + int id; + synchronized (_queueIdLock) + { + id = _queueId++; + } + return new AMQShortString("tmp_" + UUID.randomUUID() + "_" + id); + } + + @Override + public AMQConnection getAMQConnection() + { + return _connection; + } + + @Override + public SaslClient getSaslClient() + { + return _saslClient; + } + + @Override + public void setSaslClient(SaslClient client) + { + _saslClient = client; + } + + /** @param delay delay in seconds (not ms) */ + @Override + void initHeartbeats(int delay) + { + if (delay > 0) + { + // FIXME: actually do something here + HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); + } + } + + @Override + public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException + { + // FIXME? + _protocolHandler.methodBodyReceived(channel, amqMethodBody, null); + } + + @Override + public void writeFrame(AMQDataBlock frame, boolean wait) + { + _ioSender.send(frame.toNioByteBuffer()); + if (wait) + { + _ioSender.flush(); + } + } + + @Override + public void setSender(Sender sender) + { + _ioSender = sender; + } + + @Override + public ConnectionTuneParameters getConnectionTuneParameters() + { + return _connectionTuneParameters; + } + + @Override + public void setConnectionTuneParameters(ConnectionTuneParameters params) + { + _connectionTuneParameters = params; + AMQConnection con = getAMQConnection(); + con.setMaximumChannelCount(params.getChannelMax()); + con.setMaximumFrameSize(params.getFrameMax()); + initHeartbeats((int) params.getHeartbeat()); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java new file mode 100644 index 0000000000..2389c9e2da --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -0,0 +1,856 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.ReadThrottleFilterBuilder; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.WriteBufferLimitFilterBuilder; +import org.apache.mina.filter.codec.ProtocolCodecException; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.executor.ExecutorFilter; +import org.apache.qpid.AMQConnectionClosedException; +import org.apache.qpid.AMQDisconnectedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQTimeoutException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.SSLConfiguration; +import org.apache.qpid.client.configuration.ClientProperties; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverHandler; +import org.apache.qpid.client.failover.FailoverState; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateWaiter; +import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.*; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.network.io.IoTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CountDownLatch; + +/** + * AMQProtocolHandler is the client side protocol handler for AMQP, it handles all protocol events received from the + * network by MINA. The primary purpose of AMQProtocolHandler is to translate the generic event model of MINA into the + * specific event model of AMQP, by revealing the type of the received events (from decoded data), and passing the + * event on to more specific handlers for the type. In this sense, it channels the richer event model of AMQP, + * expressed in terms of methods and so on, through the cruder, general purpose event model of MINA, expressed in + * terms of "message received" and so on. + * + *

    There is a 1:1 mapping between an AMQProtocolHandler and an {@link AMQConnection}. The connection class is + * exposed to the end user of the AMQP client API, and also implements the JMS Connection API, so provides the public + * API calls through which an individual connection can be manipulated. This protocol handler talks to the network + * through MINA, in a behind the scenes role; it is not an exposed part of the client API. + * + *

    There is a 1:many mapping between an AMQProtocolHandler and a set of {@link AMQSession}s. At the MINA level, + * there is one session per connection. At the AMQP level there can be many channels which are also called sessions in + * JMS parlance. The {@link AMQSession}s are managed through an {@link AMQProtocolSession} instance. The protocol + * session is similar to the MINA per-connection session, except that it can span the lifecycle of multiple MINA sessions + * in the event of failover. See below for more information about this. + * + *

    Mina provides a session container that can be used to store/retrieve arbitrary objects as String named + * attributes. A more convenient, type-safe, container for session data is provided in the form of + * {@link AMQProtocolSession}. + * + *

    A common way to use MINA is to have a single instance of the event handler, and for MINA to pass in its session + * object with every event, and for per-connection data to be held in the MINA session (perhaps using a type-safe wrapper + * as described above). This event handler is different, because dealing with failover complicates things. To the + * end client of an AMQConnection, a failed over connection is still handled through the same connection instance, but + * behind the scenes a new transport connection, and MINA session will have been created. The MINA session object cannot + * be used to track the state of the fail-over process, because it is destroyed and a new one is created, as the old + * connection is shutdown and a new one created. For this reason, an AMQProtocolHandler is created per AMQConnection + * and the protocol session data is held outside of the MINA IOSession. + * + *

    This handler is responsibile for setting up the filter chain to filter all events for this handler through. + * The filter chain is set up as a stack of event handers that perform the following functions (working upwards from + * the network traffic at the bottom), handing off incoming events to an asynchronous thread pool to do the work, + * optionally handling secure sockets encoding/decoding, encoding/decoding the AMQP format itself. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Create the filter chain to filter this handlers events. + * {@link ProtocolCodecFilter}, {@link SSLContextFactory}, {@link SSLFilter}, {@link ReadWriteThreadModel}. + * + *
    Maintain fail-over state. + *
    + *
    + * + * @todo Explain the system property: amqj.shared_read_write_pool. How does putting the protocol codec filter before the + * async write filter make it a shared pool? The pooling filter uses the same thread pool for reading and writing + * anyway, see {@link org.apache.qpid.pool.PoolingFilter}, docs for comments. Will putting the protocol codec + * filter before it mean not doing the read/write asynchronously but in the main filter thread? + * @todo Use a single handler instance, by shifting everything to do with the 'protocol session' state, including + * failover state, into AMQProtocolSession, and tracking that from AMQConnection? The lifecycles of + * AMQProtocolSesssion and AMQConnection will be the same, so if there is high cohesion between them, they could + * be merged, although there is sense in keeping the session model seperate. Will clarify things by having data + * held per protocol handler, per protocol session, per network connection, per channel, in seperate classes, so + * that lifecycles of the fields match lifecycles of their containing objects. + */ +public class AMQProtocolHandler extends IoHandlerAdapter +{ + /** Used for debugging. */ + private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandler.class); + private static final Logger _protocolLogger = LoggerFactory.getLogger("qpid.protocol"); + private static final boolean PROTOCOL_DEBUG = (System.getProperty("amqj.protocol.logging.level") != null); + + /** + * The connection that this protocol handler is associated with. There is a 1-1 mapping between connection + * instances and protocol handler instances. + */ + private AMQConnection _connection; + + /** Our wrapper for a protocol session that provides access to session values in a typesafe manner. */ + private volatile AMQProtocolSession _protocolSession; + + /** Holds the state of the protocol session. */ + private AMQStateManager _stateManager = new AMQStateManager(); + + /** Holds the method listeners, */ + private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); + + /** + * We create the failover handler when the session is created since it needs a reference to the IoSession in order + * to be able to send errors during failover back to the client application. The session won't be available in the + * case where we failing over due to a Connection.Redirect message from the broker. + */ + private FailoverHandler _failoverHandler; + + /** + * This flag is used to track whether failover is being attempted. It is used to prevent the application constantly + * attempting failover where it is failing. + */ + private FailoverState _failoverState = FailoverState.NOT_STARTED; + + /** Used to provide a condition to wait upon for operations that are required to wait for failover to complete. */ + private CountDownLatch _failoverLatch; + + /** The last failover exception that occured */ + private FailoverException _lastFailoverException; + + /** Defines the default timeout to use for synchronous protocol commands. */ + private final long DEFAULT_SYNC_TIMEOUT = Long.getLong("amqj.default_syncwrite_timeout", 1000 * 30); + + /** Object to lock on when changing the latch */ + private Object _failoverLatchChange = new Object(); + + /** + * Creates a new protocol handler, associated with the specified client connection instance. + * + * @param con The client connection that this is the event handler for. + */ + public AMQProtocolHandler(AMQConnection con) + { + _connection = con; + } + + /** + * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the + * session, which filters the events handled by this handler. The filter chain consists of, handing off events + * to an asynchronous thread pool, optionally encoding/decoding ssl, encoding/decoding AMQP. + * + * @param session The MINA session. + * + * @throws Exception Any underlying exceptions are allowed to fall through to MINA. + */ + public void sessionCreated(IoSession session) throws Exception + { + _logger.debug("Protocol session created for session " + System.identityHashCode(session)); + _failoverHandler = new FailoverHandler(this, session); + + final ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false)); + + if (Boolean.getBoolean("amqj.shared_read_write_pool")) + { + session.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf); + } + else + { + session.getFilterChain().addLast("protocolFilter", pcf); + } + // we only add the SSL filter where we have an SSL connection + if (_connection.getSSLConfiguration() != null) + { + SSLConfiguration sslConfig = _connection.getSSLConfiguration(); + SSLContextFactory sslFactory = + new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); + SSLFilter sslFilter = new SSLFilter(sslFactory.buildClientContext()); + sslFilter.setUseClientMode(true); + session.getFilterChain().addBefore("protocolFilter", "ssl", sslFilter); + } + + try + { + ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); + threadModel.getAsynchronousReadFilter().createNewJobForSession(session); + threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); + } + catch (RuntimeException e) + { + _logger.error(e.getMessage(), e); + } + + if (Boolean.getBoolean(ClientProperties.PROTECTIO_PROP_NAME)) + { + try + { + //Add IO Protection Filters + IoFilterChain chain = session.getFilterChain(); + + session.getFilterChain().addLast("tempExecutorFilterForFilterBuilder", new ExecutorFilter()); + + ReadThrottleFilterBuilder readfilter = new ReadThrottleFilterBuilder(); + readfilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty( + ClientProperties.READ_BUFFER_LIMIT_PROP_NAME, ClientProperties.READ_BUFFER_LIMIT_DEFAULT))); + readfilter.attach(chain); + + WriteBufferLimitFilterBuilder writefilter = new WriteBufferLimitFilterBuilder(); + writefilter.setMaximumConnectionBufferSize(Integer.parseInt(System.getProperty( + ClientProperties.WRITE_BUFFER_LIMIT_PROP_NAME, ClientProperties.WRITE_BUFFER_LIMIT_DEFAULT))); + writefilter.attach(chain); + session.getFilterChain().remove("tempExecutorFilterForFilterBuilder"); + + _logger.info("Using IO Read/Write Filter Protection"); + } + catch (Exception e) + { + _logger.error("Unable to attach IO Read/Write Filter Protection :" + e.getMessage()); + } + } + _protocolSession = new AMQProtocolSession(this, session, _connection); + + _stateManager.setProtocolSession(_protocolSession); + + _protocolSession.init(); + } + + /** + * Called when we want to create a new IoTransport session + * @param brokerDetail + */ + public void createIoTransportSession(BrokerDetails brokerDetail) + { + _protocolSession = new AMQProtocolSession(this, _connection); + _stateManager.setProtocolSession(_protocolSession); + IoTransport.connect_0_9(getProtocolSession(), + brokerDetail.getHost(), + brokerDetail.getPort(), + brokerDetail.useSSL()); + _protocolSession.init(); + } + + /** + * Called when the network connection is closed. This can happen, either because the client explicitly requested + * that the connection be closed, in which case nothing is done, or because the connection died. In the case + * where the connection died, an attempt to failover automatically to a new connection may be started. The failover + * process will be started, provided that it is the clients policy to allow failover, and provided that a failover + * has not already been started or failed. + * + *

    It is important to note that when the connection dies this method may be called or {@link #exceptionCaught} + * may be called first followed by this method. This depends on whether the client was trying to send data at the + * time of the failure. + * + * @param session The MINA session. + * + * @todo Clarify: presumably exceptionCaught is called when the client is sending during a connection failure and + * not otherwise? The above comment doesn't make that clear. + */ + public void sessionClosed(IoSession session) + { + if (_connection.isClosed()) + { + _logger.debug("Session closed called by client"); + } + else + { + _logger.debug("Session closed called with failover state currently " + _failoverState); + + // reconnetablility was introduced here so as not to disturb the client as they have made their intentions + // known through the policy settings. + + if ((_failoverState != FailoverState.IN_PROGRESS) && _connection.failoverAllowed()) + { + _logger.debug("FAILOVER STARTING"); + if (_failoverState == FailoverState.NOT_STARTED) + { + _failoverState = FailoverState.IN_PROGRESS; + startFailoverThread(); + } + else + { + _logger.debug("Not starting failover as state currently " + _failoverState); + } + } + else + { + _logger.debug("Failover not allowed by policy."); // or already in progress? + + if (_logger.isDebugEnabled()) + { + _logger.debug(_connection.getFailoverPolicy().toString()); + } + + if (_failoverState != FailoverState.IN_PROGRESS) + { + _logger.debug("sessionClose() not allowed to failover"); + _connection.exceptionReceived(new AMQDisconnectedException( + "Server closed connection and reconnection " + "not permitted.", null)); + } + else + { + _logger.debug("sessionClose() failover in progress"); + } + } + } + + _logger.debug("Protocol Session [" + this + "] closed"); + } + + /** See {@link FailoverHandler} to see rationale for separate thread. */ + private void startFailoverThread() + { + Thread failoverThread = new Thread(_failoverHandler); + failoverThread.setName("Failover"); + // Do not inherit daemon-ness from current thread as this can be a daemon + // thread such as a AnonymousIoService thread. + failoverThread.setDaemon(false); + failoverThread.start(); + } + + public void sessionIdle(IoSession session, IdleStatus status) throws Exception + { + _logger.debug("Protocol Session [" + this + ":" + session + "] idle: " + status); + if (IdleStatus.WRITER_IDLE.equals(status)) + { + // write heartbeat frame: + _logger.debug("Sent heartbeat"); + session.write(HeartbeatBody.FRAME); + HeartbeatDiagnostics.sent(); + } + else if (IdleStatus.READER_IDLE.equals(status)) + { + // failover: + HeartbeatDiagnostics.timeout(); + _logger.warn("Timed out while waiting for heartbeat from peer."); + session.close(); + } + } + + /** + * Invoked when any exception is thrown by a user IoHandler implementation or by MINA. If the cause is an + * IOException, MINA will close the connection automatically. + * + * @param session The MINA session. + * @param cause The exception that triggered this event. + */ + public void exceptionCaught(IoSession session, Throwable cause) + { + if (_failoverState == FailoverState.NOT_STARTED) + { + // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) + if ((cause instanceof AMQConnectionClosedException) || cause instanceof IOException) + { + _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); + // this will attemp failover + + sessionClosed(session); + } + else + { + + if (cause instanceof ProtocolCodecException) + { + _logger.info("Protocol Exception caught NOT going to attempt failover as " + + "cause isn't AMQConnectionClosedException: " + cause, cause); + + AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); + propagateExceptionToAllWaiters(amqe); + } + _connection.exceptionReceived(cause); + + } + + // FIXME Need to correctly handle other exceptions. Things like ... + // if (cause instanceof AMQChannelClosedException) + // which will cause the JMSSession to end due to a channel close and so that Session needs + // to be removed from the map so we can correctly still call close without an exception when trying to close + // the server closed session. See also CloseChannelMethodHandler as the sessionClose is never called on exception + } + // we reach this point if failover was attempted and failed therefore we need to let the calling app + // know since we cannot recover the situation + else if (_failoverState == FailoverState.FAILED) + { + _logger.error("Exception caught by protocol handler: " + cause, cause); + + // we notify the state manager of the error in case we have any clients waiting on a state + // change. Those "waiters" will be interrupted and can handle the exception + AMQException amqe = new AMQException("Protocol handler error: " + cause, cause); + propagateExceptionToAllWaiters(amqe); + _connection.exceptionReceived(cause); + } + } + + /** + * There are two cases where we have other threads potentially blocking for events to be handled by this class. + * These are for the state manager (waiting for a state change) or a frame listener (waiting for a particular type + * of frame to arrive). When an error occurs we need to notify these waiters so that they can react appropriately. + * + * This should be called only when the exception is fatal for the connection. + * + * @param e the exception to propagate + * + * @see #propagateExceptionToFrameListeners + * @see #propagateExceptionToStateWaiters + */ + public void propagateExceptionToAllWaiters(Exception e) + { + propagateExceptionToFrameListeners(e); + propagateExceptionToStateWaiters(e); + } + + /** + * This caters for the case where we only need to propogate an exception to the the frame listeners to interupt any + * protocol level waits. + * + * This will would normally be used to notify all Frame Listeners that Failover is about to occur and they should + * stop waiting and relinquish the Failover lock {@see FailoverHandler}. + * + * Once the {@link FailoverHandler} has re-established the connection then the listeners will be able to re-attempt + * their protocol request and so listen again for the correct frame. + * + * @param e the exception to propagate + */ + public void propagateExceptionToFrameListeners(Exception e) + { + synchronized (_frameListeners) + { + if (!_frameListeners.isEmpty()) + { + final Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener ml = (AMQMethodListener) it.next(); + ml.error(e); + } + } + } + } + + /** + * This caters for the case where we only need to propogate an exception to the the state manager to interupt any + * thing waiting for a state change. + * + * Currently (2008-07-15) the state manager is only used during 0-8/0-9 Connection establishement. + * + * Normally the state manager would not need to be notified without notifiying the frame listeners so in normal + * cases {@link #propagateExceptionToAllWaiters} would be the correct choice. + * + * @param e the exception to propagate + */ + public void propagateExceptionToStateWaiters(Exception e) + { + getStateManager().error(e); + } + + public void notifyFailoverStarting() + { + // Set the last exception in the sync block to ensure the ordering with add. + // either this gets done and the add does the ml.error + // or the add completes first and the iterator below will do ml.error + synchronized (_frameListeners) + { + _lastFailoverException = new FailoverException("Failing over about to start"); + } + + //Only notify the Frame listeners that failover is going to occur as the State listeners shouldn't be + // interupted unless failover cannot restore the state. + propagateExceptionToFrameListeners(_lastFailoverException); + } + + public void failoverInProgress() + { + _lastFailoverException = null; + } + + private static int _messageReceivedCount; + + public void messageReceived(IoSession session, Object message) throws Exception + { + if (PROTOCOL_DEBUG) + { + _protocolLogger.info(String.format("RECV: [%s] %s", this, message)); + } + + if(message instanceof AMQFrame) + { + final boolean debug = _logger.isDebugEnabled(); + final long msgNumber = ++_messageReceivedCount; + + if (debug && ((msgNumber % 1000) == 0)) + { + _logger.debug("Received " + _messageReceivedCount + " protocol messages"); + } + + AMQFrame frame = (AMQFrame) message; + + final AMQBody bodyFrame = frame.getBodyFrame(); + + HeartbeatDiagnostics.received(bodyFrame instanceof HeartbeatBody); + + bodyFrame.handle(frame.getChannel(), _protocolSession); + + _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); + } + else if (message instanceof ProtocolInitiation) + { + // We get here if the server sends a response to our initial protocol header + // suggesting an alternate ProtocolVersion; the server will then close the + // connection. + ProtocolInitiation protocolInit = (ProtocolInitiation) message; + ProtocolVersion pv = protocolInit.checkVersion(); + getConnection().setProtocolVersion(pv); + + // get round a bug in old versions of qpid whereby the connection is not closed + _stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + } + + public void methodBodyReceived(final int channelId, final AMQBody bodyFrame, IoSession session)//, final IoSession session) + throws AMQException + { + + if (_logger.isDebugEnabled()) + { + _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + bodyFrame); + } + + final AMQMethodEvent evt = + new AMQMethodEvent(channelId, (AMQMethodBody) bodyFrame); + + try + { + + boolean wasAnyoneInterested = getStateManager().methodReceived(evt); + synchronized (_frameListeners) + { + if (!_frameListeners.isEmpty()) + { + //This iterator is safe from the error state as the frame listeners always add before they send so their + // will be ready and waiting for this response. + Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener listener = (AMQMethodListener) it.next(); + wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; + } + } + } + if (!wasAnyoneInterested) + { + throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + + _frameListeners, null); + } + } + catch (AMQException e) + { + propagateExceptionToFrameListeners(e); + + exceptionCaught(session, e); + } + + } + + private static int _messagesOut; + + public void messageSent(IoSession session, Object message) throws Exception + { + if (PROTOCOL_DEBUG) + { + _protocolLogger.debug(String.format("SEND: [%s] %s", this, message)); + } + + final long sentMessages = _messagesOut++; + + final boolean debug = _logger.isDebugEnabled(); + + if (debug && ((sentMessages % 1000) == 0)) + { + _logger.debug("Sent " + _messagesOut + " protocol messages"); + } + + _connection.bytesSent(session.getWrittenBytes()); + } + + public StateWaiter createWaiter(Set states) throws AMQException + { + return getStateManager().createWaiter(states); + } + + /** + * Convenience method that writes a frame to the protocol session. Equivalent to calling + * getProtocolSession().write(). + * + * @param frame the frame to write + */ + public void writeFrame(AMQDataBlock frame) + { + _protocolSession.writeFrame(frame); + } + + public void writeFrame(AMQDataBlock frame, boolean wait) + { + _protocolSession.writeFrame(frame, wait); + } + + /** + * Convenience method that writes a frame to the protocol session and waits for a particular response. Equivalent to + * calling getProtocolSession().write() then waiting for the response. + * + * @param frame + * @param listener the blocking listener. Note the calling thread will block. + */ + public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener) + throws AMQException, FailoverException + { + return writeCommandFrameAndWaitForReply(frame, listener, DEFAULT_SYNC_TIMEOUT); + } + + /** + * Convenience method that writes a frame to the protocol session and waits for a particular response. Equivalent to + * calling getProtocolSession().write() then waiting for the response. + * + * @param frame + * @param listener the blocking listener. Note the calling thread will block. + */ + public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener, + long timeout) throws AMQException, FailoverException + { + try + { + synchronized (_frameListeners) + { + if (_lastFailoverException != null) + { + throw _lastFailoverException; + } + + if(_stateManager.getCurrentState() == AMQState.CONNECTION_CLOSED) + { + Exception e = _stateManager.getLastException(); + if (e != null) + { + if (e instanceof AMQException) + { + AMQException amqe = (AMQException) e; + + throw amqe.cloneForCurrentThread(); + } + else + { + throw new AMQException(AMQConstant.INTERNAL_ERROR, e.getMessage(), e); + } + } + } + + _frameListeners.add(listener); + //FIXME: At this point here we should check or before add we should check _stateManager is in an open + // state so as we don't check we are likely just to time out here as I believe is being seen in QPID-1255 + } + _protocolSession.writeFrame(frame); + + return listener.blockForFrame(timeout); + // When control resumes before this line, a reply will have been received + // that matches the criteria defined in the blocking listener + } + finally + { + // If we don't removeKey the listener then no-one will + _frameListeners.remove(listener); + } + + } + + /** More convenient method to write a frame and wait for it's response. */ + public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass) throws AMQException, FailoverException + { + return syncWrite(frame, responseClass, DEFAULT_SYNC_TIMEOUT); + } + + /** More convenient method to write a frame and wait for it's response. */ + public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass, long timeout) throws AMQException, FailoverException + { + return writeCommandFrameAndWaitForReply(frame, new SpecificMethodFrameListener(frame.getChannel(), responseClass), + timeout); + } + + public void closeSession(AMQSession session) throws AMQException + { + _protocolSession.closeSession(session); + } + + /** + * Closes the connection. + * + *

    If a failover exception occurs whilst closing the connection it is ignored, as the connection is closed + * anyway. + * + * @param timeout The timeout to wait for an acknowledgement to the close request. + * + * @throws AMQException If the close fails for any reason. + */ + public void closeConnection(long timeout) throws AMQException + { + getStateManager().changeState(AMQState.CONNECTION_CLOSING); + + ConnectionCloseBody body = _protocolSession.getMethodRegistry().createConnectionCloseBody(AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client is closing the connection."), 0, 0); + + final AMQFrame frame = body.generateFrame(0); + + try + { + syncWrite(frame, ConnectionCloseOkBody.class, timeout); + _protocolSession.closeProtocolSession(); + } + catch (AMQTimeoutException e) + { + _protocolSession.closeProtocolSession(false); + } + catch (FailoverException e) + { + _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + } + } + + /** @return the number of bytes read from this protocol session */ + public long getReadBytes() + { + return _protocolSession.getIoSession().getReadBytes(); + } + + /** @return the number of bytes written to this protocol session */ + public long getWrittenBytes() + { + return _protocolSession.getIoSession().getWrittenBytes(); + } + + public void failover(String host, int port) + { + _failoverHandler.setHost(host); + _failoverHandler.setPort(port); + // see javadoc for FailoverHandler to see rationale for separate thread + startFailoverThread(); + } + + public void blockUntilNotFailingOver() throws InterruptedException + { + synchronized(_failoverLatchChange) + { + if (_failoverLatch != null) + { + _failoverLatch.await(); + } + } + } + + public AMQShortString generateQueueName() + { + return _protocolSession.generateQueueName(); + } + + public CountDownLatch getFailoverLatch() + { + return _failoverLatch; + } + + public void setFailoverLatch(CountDownLatch failoverLatch) + { + synchronized (_failoverLatchChange) + { + _failoverLatch = failoverLatch; + } + } + + public AMQConnection getConnection() + { + return _connection; + } + + public AMQStateManager getStateManager() + { + return _stateManager; + } + + public void setStateManager(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + FailoverState getFailoverState() + { + return _failoverState; + } + + public void setFailoverState(FailoverState failoverState) + { + _failoverState = failoverState; + } + + public byte getProtocolMajorVersion() + { + return _protocolSession.getProtocolMajorVersion(); + } + + public byte getProtocolMinorVersion() + { + return _protocolSession.getProtocolMinorVersion(); + } + + public MethodRegistry getMethodRegistry() + { + return _protocolSession.getMethodRegistry(); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolSession.getProtocolVersion(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java new file mode 100644 index 0000000000..5e12a5e6f8 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -0,0 +1,545 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import org.apache.commons.lang.StringUtils; +import org.apache.mina.common.CloseFuture; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.WriteFuture; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.security.sasl.SaslClient; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.ConnectionTuneParameters; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.message.UnprocessedMessage_0_8; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.client.handler.ClientMethodDispatcherImpl; + +/** + * Wrapper for protocol session that provides type-safe access to session attributes.

    The underlying protocol + * session is still available but clients should not use it to obtain session attributes. + */ +public class AMQProtocolSession implements AMQVersionAwareProtocolSession +{ + protected static final int LAST_WRITE_FUTURE_JOIN_TIMEOUT = 1000 * 60 * 2; + + protected static final Logger _logger = LoggerFactory.getLogger(AMQProtocolSession.class); + + public static final String PROTOCOL_INITIATION_RECEIVED = "ProtocolInitiatiionReceived"; + + protected static final String CONNECTION_TUNE_PARAMETERS = "ConnectionTuneParameters"; + + protected static final String AMQ_CONNECTION = "AMQConnection"; + + protected static final String SASL_CLIENT = "SASLClient"; + + protected final IoSession _minaProtocolSession; + + protected WriteFuture _lastWriteFuture; + + /** + * The handler from which this session was created and which is used to handle protocol events. We send failover + * events to the handler. + */ + protected final AMQProtocolHandler _protocolHandler; + + /** Maps from the channel id to the AMQSession that it represents. */ + protected ConcurrentMap _channelId2SessionMap = new ConcurrentHashMap(); + + protected ConcurrentMap _closingChannels = new ConcurrentHashMap(); + + /** + * Maps from a channel id to an unprocessed message. This is used to tie together the JmsDeliverBody (which arrives + * first) with the subsequent content header and content bodies. + */ + private final ConcurrentMap _channelId2UnprocessedMsgMap = new ConcurrentHashMap(); + private final UnprocessedMessage[] _channelId2UnprocessedMsgArray = new UnprocessedMessage[16]; + + /** Counter to ensure unique queue names */ + protected int _queueId = 1; + protected final Object _queueIdLock = new Object(); + + private ProtocolVersion _protocolVersion; +// private VersionSpecificRegistry _registry = +// MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion()); + + private MethodRegistry _methodRegistry = + MethodRegistry.getMethodRegistry(ProtocolVersion.getLatestSupportedVersion()); + + private MethodDispatcher _methodDispatcher; + + protected final AMQConnection _connection; + + private static final int FAST_CHANNEL_ACCESS_MASK = 0xFFFFFFF0; + + public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) + { + _protocolHandler = protocolHandler; + _minaProtocolSession = protocolSession; + _minaProtocolSession.setAttachment(this); + // properties of the connection are made available to the event handlers + _minaProtocolSession.setAttribute(AMQ_CONNECTION, connection); + // fixme - real value needed + _minaProtocolSession.setWriteTimeout(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + _protocolVersion = connection.getProtocolVersion(); + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), + this); + _connection = connection; + + } + + public AMQProtocolSession(AMQProtocolHandler protocolHandler, AMQConnection connection) + { + _protocolHandler = protocolHandler; + _minaProtocolSession = null; + _protocolVersion = connection.getProtocolVersion(); + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(ProtocolVersion.getLatestSupportedVersion(), + this); + _connection = connection; + } + + public void init() + { + // start the process of setting up the connection. This is the first place that + // data is written to the server. + _minaProtocolSession.write(new ProtocolInitiation(_connection.getProtocolVersion())); + } + + public String getClientID() + { + try + { + return getAMQConnection().getClientID(); + } + catch (JMSException e) + { + // we never throw a JMSException here + return null; + } + } + + public void setClientID(String clientID) throws JMSException + { + getAMQConnection().setClientID(clientID); + } + + public AMQStateManager getStateManager() + { + return _protocolHandler.getStateManager(); + } + + public String getVirtualHost() + { + return getAMQConnection().getVirtualHost(); + } + + public String getUsername() + { + return getAMQConnection().getUsername(); + } + + public String getPassword() + { + return getAMQConnection().getPassword(); + } + + public IoSession getIoSession() + { + return _minaProtocolSession; + } + + public SaslClient getSaslClient() + { + return (SaslClient) _minaProtocolSession.getAttribute(SASL_CLIENT); + } + + /** + * Store the SASL client currently being used for the authentication handshake + * + * @param client if non-null, stores this in the session. if null clears any existing client being stored + */ + public void setSaslClient(SaslClient client) + { + if (client == null) + { + _minaProtocolSession.removeAttribute(SASL_CLIENT); + } + else + { + _minaProtocolSession.setAttribute(SASL_CLIENT, client); + } + } + + public ConnectionTuneParameters getConnectionTuneParameters() + { + return (ConnectionTuneParameters) _minaProtocolSession.getAttribute(CONNECTION_TUNE_PARAMETERS); + } + + public void setConnectionTuneParameters(ConnectionTuneParameters params) + { + _minaProtocolSession.setAttribute(CONNECTION_TUNE_PARAMETERS, params); + AMQConnection con = getAMQConnection(); + con.setMaximumChannelCount(params.getChannelMax()); + con.setMaximumFrameSize(params.getFrameMax()); + initHeartbeats((int) params.getHeartbeat()); + } + + /** + * Callback invoked from the BasicDeliverMethodHandler when a message has been received. This is invoked on the MINA + * dispatcher thread. + * + * @param message + * + * @throws AMQException if this was not expected + */ + public void unprocessedMessageReceived(final int channelId, UnprocessedMessage message) throws AMQException + { + if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + _channelId2UnprocessedMsgArray[channelId] = message; + } + else + { + _channelId2UnprocessedMsgMap.put(channelId, message); + } + } + + public void contentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException + { + final UnprocessedMessage_0_8 msg = (UnprocessedMessage_0_8) ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0 ? _channelId2UnprocessedMsgArray[channelId] + : _channelId2UnprocessedMsgMap.get(channelId)); + + if (msg == null) + { + throw new AMQException(null, "Error: received content header without having received a BasicDeliver frame first on session:" + this, null); + } + + if (msg.getContentHeader() != null) + { + throw new AMQException(null, "Error: received duplicate content header or did not receive correct number of content body frames on session:" + this, null); + } + + msg.setContentHeader(contentHeader); + if (contentHeader.bodySize == 0) + { + deliverMessageToAMQSession(channelId, msg); + } + } + + public void contentBodyReceived(final int channelId, ContentBody contentBody) throws AMQException + { + UnprocessedMessage_0_8 msg; + final boolean fastAccess = (channelId & FAST_CHANNEL_ACCESS_MASK) == 0; + if (fastAccess) + { + msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgArray[channelId]; + } + else + { + msg = (UnprocessedMessage_0_8) _channelId2UnprocessedMsgMap.get(channelId); + } + + if (msg == null) + { + throw new AMQException(null, "Error: received content body without having received a JMSDeliver frame first", null); + } + + if (msg.getContentHeader() == null) + { + if (fastAccess) + { + _channelId2UnprocessedMsgArray[channelId] = null; + } + else + { + _channelId2UnprocessedMsgMap.remove(channelId); + } + throw new AMQException(null, "Error: received content body without having received a ContentHeader frame first", null); + } + + msg.receiveBody(contentBody); + + if (msg.isAllBodyDataReceived()) + { + deliverMessageToAMQSession(channelId, msg); + } + } + + public void heartbeatBodyReceived(int channelId, HeartbeatBody body) throws AMQException + { + + } + + /** + * Deliver a message to the appropriate session, removing the unprocessed message from our map + * + * @param channelId the channel id the message should be delivered to + * @param msg the message + */ + private void deliverMessageToAMQSession(int channelId, UnprocessedMessage msg) + { + AMQSession session = getSession(channelId); + session.messageReceived(msg); + if ((channelId & FAST_CHANNEL_ACCESS_MASK) == 0) + { + _channelId2UnprocessedMsgArray[channelId] = null; + } + else + { + _channelId2UnprocessedMsgMap.remove(channelId); + } + } + + protected AMQSession getSession(int channelId) + { + return _connection.getSession(channelId); + } + + /** + * Convenience method that writes a frame to the protocol session. Equivalent to calling + * getProtocolSession().write(). + * + * @param frame the frame to write + */ + public void writeFrame(AMQDataBlock frame) + { + writeFrame(frame, false); + } + + public void writeFrame(AMQDataBlock frame, boolean wait) + { + WriteFuture f = _minaProtocolSession.write(frame); + if (wait) + { + // fixme -- time out? + f.join(); + } + else + { + _lastWriteFuture = f; + } + } + + /** + * Starts the process of closing a session + * + * @param session the AMQSession being closed + */ + public void closeSession(AMQSession session) + { + _logger.debug("closeSession called on protocol session for session " + session.getChannelId()); + final int channelId = session.getChannelId(); + if (channelId <= 0) + { + throw new IllegalArgumentException("Attempt to close a channel with id < 0"); + } + // we need to know when a channel is closing so that we can respond + // with a channel.close frame when we receive any other type of frame + // on that channel + _closingChannels.putIfAbsent(channelId, session); + } + + /** + * Called from the ChannelClose handler when a channel close frame is received. This method decides whether this is + * a response or an initiation. The latter case causes the AMQSession to be closed and an exception to be thrown if + * appropriate. + * + * @param channelId the id of the channel (session) + * + * @return true if the client must respond to the server, i.e. if the server initiated the channel close, false if + * the channel close is just the server responding to the client's earlier request to close the channel. + */ + public boolean channelClosed(int channelId, AMQConstant code, String text) throws AMQException + { + + // if this is not a response to an earlier request to close the channel + if (_closingChannels.remove(channelId) == null) + { + final AMQSession session = getSession(channelId); + try + { + session.closed(new AMQException(code, text, null)); + } + catch (JMSException e) + { + throw new AMQException(null, "JMSException received while closing session", e); + } + + return true; + } + else + { + return false; + } + } + + public AMQConnection getAMQConnection() + { + return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION); + } + + public void closeProtocolSession() throws AMQException + { + closeProtocolSession(true); + } + + public void closeProtocolSession(boolean waitLast) throws AMQException + { + _logger.debug("Waiting for last write to join."); + if (waitLast && (_lastWriteFuture != null)) + { + _lastWriteFuture.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + } + + _logger.debug("Closing protocol session"); + + final CloseFuture future = _minaProtocolSession.close(); + + // There is no recovery we can do if the join on the close failes so simply mark the connection CLOSED + // then wait for the connection to close. + // ritchiem: Could this release BlockingWaiters to early? The close has been done as much as possible so any + // error now shouldn't matter. + + _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); + future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + } + + public void failover(String host, int port) + { + _protocolHandler.failover(host, port); + } + + protected AMQShortString generateQueueName() + { + int id; + synchronized (_queueIdLock) + { + id = _queueId++; + } + // get rid of / and : and ; from address for spec conformance + String localAddress = StringUtils.replaceChars(_minaProtocolSession.getLocalAddress().toString(), "/;:", ""); + + return new AMQShortString("tmp_" + localAddress + "_" + id); + } + + /** @param delay delay in seconds (not ms) */ + void initHeartbeats(int delay) + { + if (delay > 0) + { + _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay); + _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.CONFIG.getTimeout(delay)); + HeartbeatDiagnostics.init(delay, HeartbeatConfig.CONFIG.getTimeout(delay)); + } + } + + public void confirmConsumerCancelled(int channelId, AMQShortString consumerTag) + { + final AMQSession session = getSession(channelId); + + session.confirmConsumerCancelled(consumerTag.toIntValue()); + } + + public void setProtocolVersion(final ProtocolVersion pv) + { + _protocolVersion = pv; + _methodRegistry = MethodRegistry.getMethodRegistry(pv); + _methodDispatcher = ClientMethodDispatcherImpl.newMethodDispatcher(pv, this); + + // _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor); + } + + public byte getProtocolMinorVersion() + { + return _protocolVersion.getMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return _protocolVersion.getMajorVersion(); + } + + public ProtocolVersion getProtocolVersion() + { + return _protocolVersion; + } + +// public VersionSpecificRegistry getRegistry() +// { +// return _registry; +// } + + public MethodRegistry getMethodRegistry() + { + return _methodRegistry; + } + + public MethodDispatcher getMethodDispatcher() + { + return _methodDispatcher; + } + + public void setTicket(int ticket, int channelId) + { + final AMQSession session = getSession(channelId); + session.setTicket(ticket); + } + + public void setMethodDispatcher(MethodDispatcher methodDispatcher) + { + _methodDispatcher = methodDispatcher; + } + + public void setFlowControl(final int channelId, final boolean active) + { + final AMQSession session = getSession(channelId); + session.setFlowControl(active); + } + + public void methodFrameReceived(final int channel, final AMQMethodBody amqMethodBody) throws AMQException + { + _protocolHandler.methodBodyReceived(channel, amqMethodBody, _minaProtocolSession); + } + + public void notifyError(Exception error) + { + _protocolHandler.propagateExceptionToAllWaiters(error); + } + + public void setSender(Sender sender) + { + // No-op, interface munging + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java new file mode 100644 index 0000000000..2bc609ebf2 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.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.client.protocol; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQTimeoutException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.util.BlockingWaiter; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; + +/** + * BlockingMethodFrameListener is a 'rendezvous' which acts as a {@link AMQMethodListener} that delegates handling of + * incoming methods to a method listener implemented as a sub-class of this and hands off the processed method or + * error to a consumer. The producer of the event does not have to wait for the consumer to take the event, so this + * differs from a 'rendezvous' in that sense. + * + *

    BlockingMethodFrameListeners are used to coordinate waiting for replies to method calls that expect a response. + * They are always used in a 'one-shot' manner, that is, to recieve just one response. Usually the caller has to register + * them as method listeners with an event dispatcher and remember to de-register them (in a finally block) once they + * have been completed. + * + *

    The {@link #processMethod} must return true on any incoming method that it handles. This indicates to + * this listeners that the method it is waiting for has arrived. Incoming methods are also filtered by channel prior to + * being passed to the {@link #processMethod} method, so responses are only received for a particular channel. The + * channel id must be passed to the constructor. + * + *

    Errors from the producer are rethrown to the consumer. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Accept notification of AMQP method events. {@link AMQMethodEvent} + *
    Delegate handling of the method to another method listener. {@link AMQMethodBody} + *
    Block until a method is handled by the delegated to handler. + *
    Propagate the most recent exception to the consumer. + *
    + * + * @todo Might be neater if this method listener simply wrapped another that provided the method handling using a + * methodRecevied method. The processMethod takes an additional channelId, however none of the implementations + * seem to use it. So wrapping the listeners is possible. + * @todo If the retrotranslator can handle it, could use a SynchronousQueue to implement this rendezvous. Need to + * check that SynchronousQueue has a non-blocking put method available. + */ +public abstract class BlockingMethodFrameListener extends BlockingWaiter implements AMQMethodListener +{ + + /** Holds the channel id for the channel upon which this listener is waiting for a response. */ + protected int _channelId; + + /** + * Creates a new method listener, that filters incoming method to just those that match the specified channel id. + * + * @param channelId The channel id to filter incoming methods with. + */ + public BlockingMethodFrameListener(int channelId) + { + _channelId = channelId; + } + + /** + * Delegates any additional handling of the incoming methods to another handler. + * + * @param channelId The channel id of the incoming method. + * @param frame The method body. + * + * @return true if the method was handled, false otherwise. + */ + public abstract boolean processMethod(int channelId, AMQMethodBody frame); + + public boolean process(AMQMethodEvent evt) + { + AMQMethodBody method = evt.getMethod(); + + return (evt.getChannelId() == _channelId) && processMethod(evt.getChannelId(), method); + } + + /** + * Informs this listener that an AMQP method has been received. + * + * @param evt The AMQP method. + * + * @return true if this listener has handled the method, false otherwise. + */ + public boolean methodReceived(AMQMethodEvent evt) + { + return received(evt); + } + + /** + * Blocks until a method is received that is handled by the delegated to method listener, or the specified timeout + * has passed. + * + * @param timeout The timeout in milliseconds. + * + * @return The AMQP method that was received. + * + * @throws AMQException + * @throws FailoverException + */ + public AMQMethodEvent blockForFrame(long timeout) throws AMQException, FailoverException + { + try + { + return (AMQMethodEvent) block(timeout); + } + finally + { + //Prevent any more errors being notified to this waiter. + close(); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java new file mode 100644 index 0000000000..35ea44a331 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java @@ -0,0 +1,61 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class HeartbeatConfig +{ + private static final Logger _logger = LoggerFactory.getLogger(HeartbeatConfig.class); + static final HeartbeatConfig CONFIG = new HeartbeatConfig(); + + /** + * The factor used to get the timeout from the delay between heartbeats. + */ + private float timeoutFactor = 2; + + HeartbeatConfig() + { + String property = System.getProperty("amqj.heartbeat.timeoutFactor"); + if (property != null) + { + try + { + timeoutFactor = Float.parseFloat(property); + } + catch (NumberFormatException e) + { + _logger.warn("Invalid timeout factor (amqj.heartbeat.timeoutFactor): " + property); + } + } + } + + float getTimeoutFactor() + { + return timeoutFactor; + } + + int getTimeout(int writeDelay) + { + return (int) (timeoutFactor * writeDelay); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java new file mode 100644 index 0000000000..d44faeab04 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java @@ -0,0 +1,121 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +class HeartbeatDiagnostics +{ + private static final Diagnostics _impl = init(); + + private static Diagnostics init() + { + return Boolean.getBoolean("amqj.heartbeat.diagnostics") ? new On() : new Off(); + } + + static void sent() + { + _impl.sent(); + } + + static void timeout() + { + _impl.timeout(); + } + + static void received(boolean heartbeat) + { + _impl.received(heartbeat); + } + + static void init(int delay, int timeout) + { + _impl.init(delay, timeout); + } + + private static interface Diagnostics + { + void sent(); + void timeout(); + void received(boolean heartbeat); + void init(int delay, int timeout); + } + + private static class On implements Diagnostics + { + private final String[] messages = new String[50]; + private int i; + + private void save(String msg) + { + messages[i++] = msg; + if(i >= messages.length){ + i = 0;//i.e. a circular buffer + } + } + + public void sent() + { + save(System.currentTimeMillis() + ": sent heartbeat"); + } + + public void timeout() + { + for(int i = 0; i < messages.length; i++) + { + if(messages[i] != null) + { + System.out.println(messages[i]); + } + } + System.out.println(System.currentTimeMillis() + ": timed out"); + } + + public void received(boolean heartbeat) + { + save(System.currentTimeMillis() + ": received " + (heartbeat ? "heartbeat" : "data")); + } + + public void init(int delay, int timeout) + { + System.out.println(System.currentTimeMillis() + ": initialised delay=" + delay + ", timeout=" + timeout); + } + } + + private static class Off implements Diagnostics + { + public void sent() + { + + } + public void timeout() + { + + } + public void received(boolean heartbeat) + { + + } + + public void init(int delay, int timeout) + { + + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java new file mode 100644 index 0000000000..93cc5e7ec3 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java @@ -0,0 +1,115 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import org.apache.mina.common.IoFilterAdapter; +import org.apache.mina.common.IoSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A MINA filter that monitors the numbers of messages pending to be sent by MINA. It outputs a message + * when a threshold has been exceeded, and has a frequency configuration so that messages are not output + * too often. + * + */ +public class ProtocolBufferMonitorFilter extends IoFilterAdapter +{ + private static final Logger _logger = LoggerFactory.getLogger(ProtocolBufferMonitorFilter.class); + + public static long DEFAULT_FREQUENCY = 5000; + + public static int DEFAULT_THRESHOLD = 3000; + + private int _bufferedMessages = 0; + + private int _threshold; + + private long _lastMessageOutputTime; + + private long _outputFrequencyInMillis; + + public ProtocolBufferMonitorFilter() + { + _threshold = DEFAULT_THRESHOLD; + _outputFrequencyInMillis = DEFAULT_FREQUENCY; + } + + public ProtocolBufferMonitorFilter(int threshold, long frequency) + { + _threshold = threshold; + _outputFrequencyInMillis = frequency; + } + + public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception + { + _bufferedMessages++; + if (_bufferedMessages > _threshold) + { + long now = System.currentTimeMillis(); + if ((now - _lastMessageOutputTime) > _outputFrequencyInMillis) + { + _logger.warn("Protocol message buffer exceeded threshold of " + _threshold + ". Current backlog: " + + _bufferedMessages); + _lastMessageOutputTime = now; + } + } + + nextFilter.messageReceived(session, message); + } + + public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception + { + _bufferedMessages--; + nextFilter.messageSent(session, message); + } + + public int getBufferedMessages() + { + return _bufferedMessages; + } + + public int getThreshold() + { + return _threshold; + } + + public void setThreshold(int threshold) + { + _threshold = threshold; + } + + public long getOutputFrequencyInMillis() + { + return _outputFrequencyInMillis; + } + + public void setOutputFrequencyInMillis(long outputFrequencyInMillis) + { + _outputFrequencyInMillis = outputFrequencyInMillis; + } + + public long getLastMessageOutputTime() + { + return _lastMessageOutputTime; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java new file mode 100644 index 0000000000..fbca444208 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security; + +import javax.security.auth.callback.CallbackHandler; + +import org.apache.qpid.client.protocol.AMQProtocolSession; + +public interface AMQCallbackHandler extends CallbackHandler +{ + void initialise(AMQProtocolSession protocolSession); +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java new file mode 100644 index 0000000000..140cbdeb75 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.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.client.security; + +import org.apache.qpid.util.FileUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user + * authentication. It is capable of reading its configuration from a properties file containing call back handler + * implementing class names for different SASL mechanism names. Instantiating this registry also has the effect of + * configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}. + * + *

    The callback configuration should be specified in a properties file, refered to by the System property + * "amp.callbackhandler.properties". The format of the properties file is: + * + *

    + * CallbackHanlder.mechanism=fully.qualified.class.name
    + * 
    + * + *

    Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a + * class that implements org.apache.qpid.client.security.AMQCallbackHanlder and provides a call back handler for the + * specified mechanism. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Parse callback properties. + *
    Provide mapping from SASL mechanisms to callback implementations. + *
    + */ +public class CallbackHandlerRegistry +{ + private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class); + + /** The name of the system property that holds the name of the callback handler properties file. */ + private static final String FILE_PROPERTY = "amq.callbackhandler.properties"; + + /** The default name of the callback handler properties resource. */ + public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/CallbackHandlerRegistry.properties"; + + /** A static reference to the singleton instance of this registry. */ + private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry(); + + /** Holds a map from SASL mechanism names to call back handlers. */ + private Map _mechanismToHandlerClassMap = new HashMap(); + + /** Holds a space delimited list of mechanisms that callback handlers exist for. */ + private String _mechanisms; + + /** + * Gets the singleton instance of this registry. + * + * @return The singleton instance of this registry. + */ + public static CallbackHandlerRegistry getInstance() + { + return _instance; + } + + /** + * Gets the callback handler class for a given SASL mechanism name. + * + * @param mechanism The SASL mechanism name. + * + * @return The callback handler class for the mechanism, or null if none is configured for that mechanism. + */ + public Class getCallbackHandlerClass(String mechanism) + { + return (Class) _mechanismToHandlerClassMap.get(mechanism); + } + + /** + * Gets a space delimited list of supported SASL mechanisms. + * + * @return A space delimited list of supported SASL mechanisms. + */ + public String getMechanisms() + { + return _mechanisms; + } + + /** + * Creates the call back handler registry from its configuration resource or file. This also has the side effect + * of configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}. + */ + private CallbackHandlerRegistry() + { + // Register any configured SASL client factories. + DynamicSaslRegistrar.registerSaslProviders(); + + String filename = System.getProperty(FILE_PROPERTY); + InputStream is = + FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, + CallbackHandlerRegistry.class.getClassLoader()); + + try + { + Properties props = new Properties(); + props.load(is); + parseProperties(props); + _logger.info("Callback handlers available for SASL mechanisms: " + _mechanisms); + } + catch (IOException e) + { + _logger.error("Error reading properties: " + e, e); + } + finally + { + if (is != null) + { + try + { + is.close(); + + } + catch (IOException e) + { + _logger.error("Unable to close properties stream: " + e, e); + } + } + } + } + + /*private InputStream openPropertiesInputStream(String filename) + { + boolean useDefault = true; + InputStream is = null; + if (filename != null) + { + try + { + is = new BufferedInputStream(new FileInputStream(new File(filename))); + useDefault = false; + } + catch (FileNotFoundException e) + { + _logger.error("Unable to read from file " + filename + ": " + e, e); + } + } + + if (useDefault) + { + is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME); + } + + return is; + }*/ + + /** + * Scans the specified properties as a mapping from IANA registered SASL mechanism to call back handler + * implementations, that provide the necessary call back handling for obtaining user log in credentials + * during authentication for the specified mechanism, and builds a map from mechanism names to handler + * classes. + * + * @param props + */ + private void parseProperties(Properties props) + { + Enumeration e = props.propertyNames(); + while (e.hasMoreElements()) + { + String propertyName = (String) e.nextElement(); + int period = propertyName.indexOf("."); + if (period < 0) + { + _logger.warn("Unable to parse property " + propertyName + " when configuring SASL providers"); + + continue; + } + + String mechanism = propertyName.substring(period + 1); + String className = props.getProperty(propertyName); + Class clazz = null; + try + { + clazz = Class.forName(className); + if (!AMQCallbackHandler.class.isAssignableFrom(clazz)) + { + _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class + + ". Skipping"); + + continue; + } + + _mechanismToHandlerClassMap.put(mechanism, clazz); + if (_mechanisms == null) + { + _mechanisms = mechanism; + } + else + { + // one time cost + _mechanisms = _mechanisms + " " + mechanism; + } + } + catch (ClassNotFoundException ex) + { + _logger.warn("Unable to load class " + className + ". Skipping that SASL provider"); + + continue; + } + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties new file mode 100644 index 0000000000..1fcfde3579 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +CallbackHandler.CRAM-MD5-HASHED=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler +CallbackHandler.CRAM-MD5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +CallbackHandler.AMQPLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +CallbackHandler.PLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java new file mode 100644 index 0000000000..2b4261b4b7 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.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.client.security; + +import org.apache.qpid.util.FileUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.SaslClientFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Security; +import java.util.Enumeration; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +/** + * DynamicSaslRegistrar provides a collection of helper methods for reading a configuration file that contains a mapping + * from SASL mechanism names to implementing client factory class names and registering a security provider with the + * Java runtime system, that uses the configured client factory implementations. + * + *

    The sasl configuration should be specified in a properties file, refered to by the System property + * "amp.dynamicsaslregistrar.properties". The format of the properties file is: + * + *

    + * mechanism=fully.qualified.class.name
    + * 
    + * + *

    Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a class that + * implements javax.security.sasl.SaslClientFactory and provides the specified mechanism. + * + *

    CRC Card
    Responsibilities Collaborations
    Parse SASL + * mechanism properties.
    Create and register security provider for SASL mechanisms.
    + */ +public class DynamicSaslRegistrar +{ + private static final Logger _logger = LoggerFactory.getLogger(DynamicSaslRegistrar.class); + + /** The name of the system property that holds the name of the SASL configuration properties. */ + private static final String FILE_PROPERTY = "amq.dynamicsaslregistrar.properties"; + + /** The default name of the SASL properties file resource. */ + public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/DynamicSaslRegistrar.properties"; + + /** Reads the properties file, and creates a dynamic security provider to register the SASL implementations with. */ + public static void registerSaslProviders() + { + _logger.debug("public static void registerSaslProviders(): called"); + + // Open the SASL properties file, using the default name is one is not specified. + String filename = System.getProperty(FILE_PROPERTY); + InputStream is = + FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, + DynamicSaslRegistrar.class.getClassLoader()); + + try + { + Properties props = new Properties(); + props.load(is); + + _logger.debug("props = " + props); + + Map> factories = parseProperties(props); + + if (factories.size() > 0) + { + // Ensure we are used before the defaults + if (Security.insertProviderAt(new JCAProvider(factories), 1) == -1) + { + _logger.error("Unable to load custom SASL providers."); + } + else + { + _logger.info("Additional SASL providers successfully registered."); + } + } + else + { + _logger.warn("No additional SASL providers registered."); + } + } + catch (IOException e) + { + _logger.error("Error reading properties: " + e, e); + } + finally + { + if (is != null) + { + try + { + is.close(); + + } + catch (IOException e) + { + _logger.error("Unable to close properties stream: " + e, e); + } + } + } + } + + /** + * Either attempts to open the specified filename as an input stream, or uses the default SASL configuration + * resource. + * + * @param filename The name of the file to get the SASL properties from, null to use the default. + * + * @return An input stream to read the dynamic SASL configuration from, or null if one could not be opened. + */ + /*private static InputStream openPropertiesInputStream(String filename) + { + InputStream is = null; + + // Flag to indicate whether the default resource should be used. By default this is true, so that the default + // is used when opening the file fails. + boolean useDefault = true; + + // Try to open the file if one was specified. + if (filename != null) + { + try + { + is = new BufferedInputStream(new FileInputStream(new File(filename))); + + // Clear the default flag because the file was succesfully opened. + useDefault = false; + } + catch (FileNotFoundException e) + { + _logger.error("Unable to read from file " + filename + ": " + e, e); + } + } + + // Load the default resource if a file was not specified, or if opening the file failed. + if (useDefault) + { + is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME); + } + + return is; + }*/ + + /** + * Parses the specified properties as a mapping from IANA registered SASL mechanism names to implementing client + * factories. If the client factories cannot be instantiated or do not implement SaslClientFactory then the + * properties refering to them are ignored. + * + * @param props The properties to scan for Sasl client factory implementations. + * + * @return A map from SASL mechanism names to implementing client factory classes. + * + * @todo Why tree map here? Do really want mechanisms in alphabetical order? Seems more likely that the declared + * order of the mechanisms is intended to be preserved, so that they are registered in the declared order of + * preference. Consider LinkedHashMap instead. + */ + private static Map> parseProperties(Properties props) + { + Enumeration e = props.propertyNames(); + + TreeMap> factoriesToRegister = + new TreeMap>(); + + while (e.hasMoreElements()) + { + String mechanism = (String) e.nextElement(); + String className = props.getProperty(mechanism); + try + { + Class clazz = Class.forName(className); + if (!(SaslClientFactory.class.isAssignableFrom(clazz))) + { + _logger.error("Class " + clazz + " does not implement " + SaslClientFactory.class + " - skipping"); + + continue; + } + + _logger.debug("Registering class "+ clazz.getName() +" for mechanism "+mechanism); + factoriesToRegister.put(mechanism, (Class) clazz); + } + catch (Exception ex) + { + _logger.error("Error instantiating SaslClientFactory calss " + className + " - skipping"); + } + } + + return factoriesToRegister; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties new file mode 100644 index 0000000000..1bff43142b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory +CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java new file mode 100644 index 0000000000..828d26ed0d --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.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.client.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.SaslClientFactory; + +import java.security.Provider; +import java.security.Security; +import java.util.Map; + +/** + * JCAProvider is a security provider for SASL client factories that is configured from a map of SASL mechanism names + * to client factories implementation class names. It is intended that the map of client factories can be read from a + * configuration file or other application configuration mechanism. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Register SASL mechanism implementations. + *
    + */ +public class JCAProvider extends Provider +{ + private static final Logger log = LoggerFactory.getLogger(JCAProvider.class); + + /** + * Creates the security provider with a map from SASL mechanisms to implementing factories. + * + * @param providerMap The map from SASL mechanims to implementing factory classes. + */ + public JCAProvider(Map> providerMap) + { + super("AMQSASLProvider-Client", 1.0, "A JCA provider that registers all " + + "AMQ SASL providers that want to be registered"); + register(providerMap); +// Security.addProvider(this); + } + + /** + * Registers client factory classes for a map of mechanism names to client factory classes. + * + * @param providerMap The map from SASL mechanims to implementing factory classes. + */ + private void register(Map> providerMap) + { + for (Map.Entry> me : providerMap.entrySet()) + { + put( "SaslClientFactory."+me.getKey(), me.getValue().getName()); + log.debug("Registered SASL Client factory for " + me.getKey() + " as " + me.getValue().getName()); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java new file mode 100644 index 0000000000..66176dac3c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security; + +import org.apache.qpid.client.protocol.AMQProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler +{ + private static final Logger _logger = LoggerFactory.getLogger(UsernameHashedPasswordCallbackHandler.class); + + private AMQProtocolSession _protocolSession; + + public void initialise(AMQProtocolSession protocolSession) + { + _protocolSession = protocolSession; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + for (int i = 0; i < callbacks.length; i++) + { + Callback cb = callbacks[i]; + if (cb instanceof NameCallback) + { + ((NameCallback) cb).setName(_protocolSession.getUsername()); + } + else if (cb instanceof PasswordCallback) + { + try + { + ((PasswordCallback) cb).setPassword(getHash(_protocolSession.getPassword())); + } + catch (NoSuchAlgorithmException e) + { + UnsupportedCallbackException uce = new UnsupportedCallbackException(cb); + uce.initCause(e); + throw uce; + } + } + else + { + throw new UnsupportedCallbackException(cb); + } + } + } + + private char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException + { + + byte[] data = text.getBytes("utf-8"); + + MessageDigest md = MessageDigest.getInstance("MD5"); + + for (byte b : data) + { + md.update(b); + } + + byte[] digest = md.digest(); + + char[] hash = new char[digest.length]; + + int index = 0; + for (byte b : digest) + { + hash[index++] = (char) b; + } + + return hash; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java new file mode 100644 index 0000000000..c50c62710f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.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.client.security; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.qpid.client.protocol.AMQProtocolSession; + +public class UsernamePasswordCallbackHandler implements AMQCallbackHandler +{ + private AMQProtocolSession _protocolSession; + + public void initialise(AMQProtocolSession protocolSession) + { + _protocolSession = protocolSession; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + for (int i = 0; i < callbacks.length; i++) + { + Callback cb = callbacks[i]; + if (cb instanceof NameCallback) + { + ((NameCallback)cb).setName(_protocolSession.getUsername()); + } + else if (cb instanceof PasswordCallback) + { + ((PasswordCallback)cb).setPassword(_protocolSession.getPassword().toCharArray()); + } + else + { + throw new UnsupportedCallbackException(cb); + } + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java new file mode 100644 index 0000000000..f8a25c630c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java @@ -0,0 +1,105 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security.amqplain; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; + +/** + * Implements the "AMQPlain" authentication protocol that uses FieldTables to send username and pwd. + * + */ +public class AmqPlainSaslClient implements SaslClient +{ + /** + * The name of this mechanism + */ + public static final String MECHANISM = "AMQPLAIN"; + + private CallbackHandler _cbh; + + public AmqPlainSaslClient(CallbackHandler cbh) + { + _cbh = cbh; + } + + public String getMechanismName() + { + return "AMQPLAIN"; + } + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(byte[] challenge) throws SaslException + { + // we do not care about the prompt or the default name + NameCallback nameCallback = new NameCallback("prompt", "defaultName"); + PasswordCallback pwdCallback = new PasswordCallback("prompt", false); + Callback[] callbacks = new Callback[]{nameCallback, pwdCallback}; + try + { + _cbh.handle(callbacks); + } + catch (Exception e) + { + throw new SaslException("Error handling SASL callbacks: " + e, e); + } + FieldTable table = FieldTableFactory.newFieldTable(); + table.setString("LOGIN", nameCallback.getName()); + table.setString("PASSWORD", new String(pwdCallback.getPassword())); + return table.getDataAsBytes(); + } + + public boolean isComplete() + { + return true; + } + + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new SaslException("Not supported"); + } + + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new SaslException("Not supported"); + } + + public Object getNegotiatedProperty(String propName) + { + return null; + } + + public void dispose() throws SaslException + { + _cbh = null; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java new file mode 100644 index 0000000000..30cc786890 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java @@ -0,0 +1,63 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.security.amqplain; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; + +public class AmqPlainSaslClientFactory implements SaslClientFactory +{ + public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh) throws SaslException + { + for (int i = 0; i < mechanisms.length; i++) + { + if (mechanisms[i].equals(AmqPlainSaslClient.MECHANISM)) + { + if (cbh == null) + { + throw new SaslException("CallbackHandler must not be null"); + } + return new AmqPlainSaslClient(cbh); + } + } + return null; + } + + public String[] getMechanismNames(Map props) + { + if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE)) + { + // returned array must be non null according to interface documentation + return new String[0]; + } + else + { + return new String[]{AmqPlainSaslClient.MECHANISM}; + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java new file mode 100644 index 0000000000..22bb1ac156 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.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.client.security.crammd5hashed; + +import org.apache.qpid.client.security.amqplain.AmqPlainSaslClient; + +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.Sasl; +import javax.security.auth.callback.CallbackHandler; +import java.util.Map; +import java.security.Security; + +public class CRAMMD5HashedSaslClientFactory implements SaslClientFactory +{ + /** The name of this mechanism */ + public static final String MECHANISM = "CRAM-MD5-HASHED"; + + + public SaslClient createSaslClient(String[] mechanisms, String authorizationId, String protocol, String serverName, Map props, CallbackHandler cbh) throws SaslException + { + for (int i = 0; i < mechanisms.length; i++) + { + if (mechanisms[i].equals(MECHANISM)) + { + if (cbh == null) + { + throw new SaslException("CallbackHandler must not be null"); + } + + String[] mechs = {"CRAM-MD5"}; + return Sasl.createSaslClient(mechs, authorizationId, protocol, serverName, props, cbh); + } + } + return null; + } + + public String[] getMechanismNames(Map props) + { + if (props != null) + { + if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE)) + { + // returned array must be non null according to interface documentation + return new String[0]; + } + } + + return new String[]{MECHANISM}; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java new file mode 100644 index 0000000000..2c99b9a97b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQMethodNotImplementedException.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.state; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.AMQException; + +public class AMQMethodNotImplementedException extends AMQException +{ + public AMQMethodNotImplementedException(AMQMethodBody body) + { + super(null, "Unexpected Method Received: " + body.getClass().getName(), null); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java new file mode 100644 index 0000000000..d32d10542f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQState.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.client.state; + +/** + * States used in the AMQ protocol. Used by the finite state machine to determine + * valid responses. + */ +public enum AMQState +{ + + CONNECTION_NOT_STARTED(1, "CONNECTION_NOT_STARTED"), + + CONNECTION_NOT_TUNED(2, "CONNECTION_NOT_TUNED"), + + CONNECTION_NOT_OPENED(3, "CONNECTION_NOT_OPENED"), + + CONNECTION_OPEN(4, "CONNECTION_OPEN"), + + CONNECTION_CLOSING(5, "CONNECTION_CLOSING"), + + CONNECTION_CLOSED(6, "CONNECTION_CLOSED"); + + + private final int _id; + + private final String _name; + + private AMQState(int id, String name) + { + _id = id; + _name = name; + } + + public String toString() + { + return "AMQState: id = " + _id + " name: " + _name; + } + + + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java new file mode 100644 index 0000000000..edef54ccd6 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.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.client.state; + +/** + * An event that is fired when the protocol state has changed. + * + */ +public class AMQStateChangedEvent +{ + private final AMQState _oldState; + + private final AMQState _newState; + + public AMQStateChangedEvent(AMQState oldState, AMQState newState) + { + _oldState = oldState; + _newState = newState; + } + + public AMQState getOldState() + { + return _oldState; + } + + public AMQState getNewState() + { + return _newState; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java new file mode 100644 index 0000000000..110471aad0 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java @@ -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. + * + */ +package org.apache.qpid.client.state; + +public interface AMQStateListener +{ + void stateChanged(AMQStateChangedEvent evt); +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java new file mode 100644 index 0000000000..f8645139f2 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.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.client.state; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * The state manager is responsible for managing the state of the protocol session.

    + * For each {@link org.apache.qpid.client.protocol.AMQProtocolHandler} there is a separate state manager. + * + * The AMQStateManager is now attached to the {@link org.apache.qpid.client.protocol.AMQProtocolHandler} and that is the sole point of reference so that + * As the {@link AMQProtocolSession} changes due to failover the AMQStateManager need not be copied around. + * + * The StateManager works by any component can wait for a state change to occur by using the following sequence. + * + *

  • StateWaiter waiter = stateManager.createWaiter(Set states); + *
  • // Perform action that will cause state change + *
  • waiter.await(); + * + * The two step process is required as there is an inherit race condition between starting a process that will cause + * the state to change and then attempting to wait for that change. The interest in the change must be first set up so + * that any asynchrous errors that occur can be delivered to the correct waiters. + */ +public class AMQStateManager implements AMQMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class); + + private AMQProtocolSession _protocolSession; + + /** The current state */ + private AMQState _currentState; + + private final Object _stateLock = new Object(); + + private static final long MAXIMUM_STATE_WAIT_TIME = Long.parseLong(System.getProperty("amqj.MaximumStateWait", "30000")); + + protected final List _waiters = new CopyOnWriteArrayList(); + private Exception _lastException; + + public AMQStateManager() + { + this(null); + } + + public AMQStateManager(AMQProtocolSession protocolSession) + { + this(AMQState.CONNECTION_NOT_STARTED, protocolSession); + } + + protected AMQStateManager(AMQState state, AMQProtocolSession protocolSession) + { + _protocolSession = protocolSession; + _currentState = state; + } + + public AMQState getCurrentState() + { + return _currentState; + } + + public void changeState(AMQState newState) throws AMQException + { + _logger.debug("State changing to " + newState + " from old state " + _currentState); + + synchronized (_stateLock) + { + _currentState = newState; + + _logger.debug("Notififying State change to " + _waiters.size() + " : " + _waiters); + + for (StateWaiter waiter : _waiters) + { + waiter.received(newState); + } + } + } + + public boolean methodReceived(AMQMethodEvent evt) throws AMQException + { + B method = evt.getMethod(); + + // StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod()); + method.execute(_protocolSession.getMethodDispatcher(), evt.getChannelId()); + return true; + } + + /** + * Setting of the ProtocolSession will be required when Failover has been successfuly compeleted. + * + * The new {@link AMQProtocolSession} that has been re-established needs to be provided as that is now the + * connection to the network. + * + * @param session The new protocol session + */ + public void setProtocolSession(AMQProtocolSession session) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Setting ProtocolSession:" + session); + } + _protocolSession = session; + } + + /** + * Propogate error to waiters + * + * @param error The error to propogate. + */ + public void error(Exception error) + { + if (_waiters.size() == 0) + { + _logger.error("No Waiters for error saving as last error:" + error.getMessage()); + _lastException = error; + } + for (StateWaiter waiter : _waiters) + { + _logger.error("Notifying Waiters(" + _waiters + ") for error:" + error.getMessage()); + waiter.error(error); + } + } + + /** + * This provides a single place that the maximum time for state change to occur can be accessed. + * It is currently set via System property amqj.MaximumStateWait + * + * @return long Milliseconds value for a timeout + */ + public long getWaitTimeout() + { + return MAXIMUM_STATE_WAIT_TIME; + } + + /** + * Create and add a new waiter to the notifcation list. + * + * @param states The waiter will attempt to wait for one of these desired set states to be achived. + * + * @return the created StateWaiter. + */ + public StateWaiter createWaiter(Set states) + { + final StateWaiter waiter; + synchronized (_stateLock) + { + waiter = new StateWaiter(this, _currentState, states); + + _waiters.add(waiter); + } + + return waiter; + } + + /** + * Remove the waiter from the notification list. + * + * @param waiter The waiter to remove. + */ + public void removeWaiter(StateWaiter waiter) + { + synchronized (_stateLock) + { + _waiters.remove(waiter); + } + } + + public Exception getLastException() + { + return _lastException; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java new file mode 100644 index 0000000000..17d04f4fa3 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.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.client.state; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.protocol.AMQMethodEvent; + +/** + * A frame listener that is informed of the protocl state when invoked and has + * the opportunity to update state. + * + */ +public interface StateAwareMethodListener +{ + + void methodReceived(AMQProtocolSession session, B body, int channelId) throws AMQException; + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java new file mode 100644 index 0000000000..4695b195d5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java @@ -0,0 +1,129 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.state; + +import org.apache.qpid.client.util.BlockingWaiter; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.AMQException; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import java.util.Set; + +/** + * This is an implementation of the {@link BlockingWaiter} to provide error handing and a waiting mechanism for state + * changes. + * + * On construction the current state and a set of States to await for is provided. + * + * When await() is called the state at constuction is compared against the awaitStates. If the state at construction is + * a desired state then await() returns immediately. + * + * Otherwise it will block for the set timeout for a desired state to be achieved. + * + * The state changes are notified via the {@link #process} method. + * + * Any notified error is handled by the BlockingWaiter and thrown from the {@link #block} method. + * + */ +public class StateWaiter extends BlockingWaiter +{ + private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class); + + Set _awaitStates; + private AMQState _startState; + private AMQStateManager _stateManager; + + /** + * + * @param stateManager The StateManager + * @param currentState + * @param awaitStates + */ + public StateWaiter(AMQStateManager stateManager, AMQState currentState, Set awaitStates) + { + _logger.info("New StateWaiter :" + currentState + ":" + awaitStates); + _stateManager = stateManager; + _awaitStates = awaitStates; + _startState = currentState; + } + + /** + * When the state is changed this StateWaiter is notified to process the change. + * + * @param state The new state that has been achieved. + * @return + */ + public boolean process(AMQState state) + { + return _awaitStates.contains(state); + } + + /** + * Await for the requried State to be achieved within the default timeout. + * @return The achieved state that was requested. + * @throws AMQException The exception that prevented the required state from being achived. + */ + public AMQState await() throws AMQException + { + return await(_stateManager.getWaitTimeout()); + } + + /** + * Await for the requried State to be achieved. + * + * It is the responsibility of this class to remove the waiter from the StateManager + * + * @param timeout The time in milliseconds to wait for any of the states to be achived. + * @return The achieved state that was requested. + * @throws AMQException The exception that prevented the required state from being achived. + */ + public AMQState await(long timeout) throws AMQException + { + try + { + if (process(_startState)) + { + return _startState; + } + + try + { + return (AMQState) block(timeout); + } + catch (FailoverException e) + { + _logger.error("Failover occured whilst waiting for states:" + _awaitStates); + + e.printStackTrace(); + return null; + } + } + finally + { + //Prevent any more errors being notified to this waiter. + close(); + + //Remove the waiter from the notifcation list in the statee manager + _stateManager.removeWaiter(this); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java new file mode 100644 index 0000000000..f0d7feb059 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java @@ -0,0 +1,41 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.state.listener; + + +import org.apache.qpid.client.protocol.BlockingMethodFrameListener; +import org.apache.qpid.framing.AMQMethodBody; + +public class SpecificMethodFrameListener extends BlockingMethodFrameListener +{ + private final Class _expectedClass; + + public SpecificMethodFrameListener(int channelId, Class expectedClass) + { + super(channelId); + _expectedClass = expectedClass; + } + + public boolean processMethod(int channelId, AMQMethodBody frame) + { + return _expectedClass.isInstance(frame); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java new file mode 100644 index 0000000000..6e47e2ce28 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.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.client.transport; + +import org.apache.qpid.jms.BrokerDetails; + +/** + * AMQNoTransportForProtocolException represents a connection failure where there is no transport medium to connect + * to the broker available. This may be the case if their is a error in the connection url, or an unsupported transport + * type is specified. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represent absence of a transport medium. + *
    + * + * @todo Error code never used. This is not an AMQException. + */ +public class AMQNoTransportForProtocolException extends AMQTransportConnectionException +{ + BrokerDetails _details; + + public AMQNoTransportForProtocolException(BrokerDetails details, String message, Throwable cause) + { + super(null, message, cause); + + _details = details; + } + + public String toString() + { + if (_details != null) + { + return super.toString() + _details.toString(); + } + else + { + return super.toString(); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java new file mode 100644 index 0000000000..6bef6216bd --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.transport; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQTransportConnectionException indicates a failure to establish a connection through the transporting medium, to + * an AMQP broker. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represent failure to connect through the transport medium. + *
    + * + * @todo Error code never used. This is not an AMQException. + */ +public class AMQTransportConnectionException extends AMQException +{ + public AMQTransportConnectionException(AMQConstant errorCode, String message, Throwable cause) + { + super(errorCode, message, cause); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java new file mode 100644 index 0000000000..7a24d6e15a --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.transport; + +import java.io.IOException; + +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.jms.BrokerDetails; + +public interface ITransportConnection +{ + void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) + throws IOException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java new file mode 100644 index 0000000000..b2f7ae8395 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java @@ -0,0 +1,131 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.transport; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.transport.socket.nio.ExistingSocketConnector; +import org.apache.mina.transport.socket.nio.SocketConnectorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; + +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.pool.ReadWriteThreadModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class SocketTransportConnection implements ITransportConnection +{ + private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); + private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; + + private SocketConnectorFactory _socketConnectorFactory; + + static interface SocketConnectorFactory + { + IoConnector newSocketConnector(); + } + + public SocketTransportConnection(SocketConnectorFactory socketConnectorFactory) + { + _socketConnectorFactory = socketConnectorFactory; + } + + public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException + { + ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers")); + + // the MINA default is currently to use the pooled allocator although this may change in future + // once more testing of the performance of the simple allocator has been done + if (!Boolean.getBoolean("amqj.enablePooledAllocator")) + { + _logger.info("Using SimpleByteBufferAllocator"); + ByteBuffer.setAllocator(new SimpleByteBufferAllocator()); + } + + final IoConnector ioConnector = _socketConnectorFactory.newSocketConnector(); + SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig(); + + // if we do not use our own thread model we get the MINA default which is to use + // its own leader-follower model + boolean readWriteThreading = Boolean.getBoolean("amqj.shared_read_write_pool"); + if (readWriteThreading) + { + cfg.setThreadModel(ReadWriteThreadModel.getInstance()); + } + + SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); + scfg.setTcpNoDelay("true".equalsIgnoreCase(System.getProperty("amqj.tcpNoDelay", "true"))); + scfg.setSendBufferSize(Integer.getInteger("amqj.sendBufferSize", DEFAULT_BUFFER_SIZE)); + _logger.info("send-buffer-size = " + scfg.getSendBufferSize()); + scfg.setReceiveBufferSize(Integer.getInteger("amqj.receiveBufferSize", DEFAULT_BUFFER_SIZE)); + _logger.info("recv-buffer-size = " + scfg.getReceiveBufferSize()); + + final InetSocketAddress address; + + if (brokerDetail.getTransport().equals(BrokerDetails.SOCKET)) + { + address = null; + + Socket socket = TransportConnection.removeOpenSocket(brokerDetail.getHost()); + + if (socket != null) + { + _logger.info("Using existing Socket:" + socket); + + ((ExistingSocketConnector) ioConnector).setOpenSocket(socket); + } + else + { + throw new IllegalArgumentException("Active Socket must be provided for broker " + + "with 'socket://' transport:" + brokerDetail); + } + } + else + { + address = new InetSocketAddress(brokerDetail.getHost(), brokerDetail.getPort()); + _logger.info("Attempting connection to " + address); + } + + + ConnectFuture future = ioConnector.connect(address, protocolHandler); + + // wait for connection to complete + if (future.join(brokerDetail.getTimeout())) + { + // we call getSession which throws an IOException if there has been an error connecting + future.getSession(); + } + else + { + throw new IOException("Timeout waiting for connection."); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java new file mode 100644 index 0000000000..6c12821c74 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -0,0 +1,335 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.transport; + +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.transport.socket.nio.ExistingSocketConnector; +import org.apache.mina.transport.socket.nio.MultiThreadSocketConnector; +import org.apache.mina.transport.socket.nio.SocketConnector; +import org.apache.mina.transport.vmpipe.VmPipeAcceptor; +import org.apache.mina.transport.vmpipe.VmPipeAddress; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.net.Socket; + +/** + * The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying + * connector, which currently always uses TCP/IP sockets. It creates the "protocol handler" which deals with MINA + * protocol events.

    Could be extended in future to support different transport types by turning this into concrete + * class/interface combo. + */ +public class TransportConnection +{ + private static ITransportConnection _instance; + + private static Map _inVmPipeAddress = new HashMap(); + private static VmPipeAcceptor _acceptor; + private static int _currentInstance = -1; + private static int _currentVMPort = -1; + + private static final int TCP = 0; + private static final int VM = 1; + private static final int SOCKET = 2; + + private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); + + private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; + + private static Map _openSocketRegister = new ConcurrentHashMap(); + + public static void registerOpenSocket(String socketID, Socket openSocket) + { + _openSocketRegister.put(socketID, openSocket); + } + + public static Socket removeOpenSocket(String socketID) + { + return _openSocketRegister.remove(socketID); + } + + public static synchronized ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException + { + int transport = getTransport(details.getTransport()); + + if (transport == -1) + { + throw new AMQNoTransportForProtocolException(details, null, null); + } + + switch (transport) + { + case SOCKET: + return new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() + { + public IoConnector newSocketConnector() + { + return new ExistingSocketConnector(); + } + }); + case TCP: + return new SocketTransportConnection(new SocketTransportConnection.SocketConnectorFactory() + { + public IoConnector newSocketConnector() + { + SocketConnector result; + // FIXME - this needs to be sorted to use the new Mina MultiThread SA. + if (Boolean.getBoolean("qpidnio")) + { + _logger.warn("Using Qpid MultiThreaded NIO - " + (System.getProperties().containsKey("qpidnio") + ? "Qpid NIO is new default" + : "Sysproperty 'qpidnio' is set")); + result = new MultiThreadSocketConnector(); + } + else + { + _logger.info("Using Mina NIO"); + result = new SocketConnector(); // non-blocking connector + } + // Don't have the connector's worker thread wait around for other connections (we only use + // one SocketConnector per connection at the moment anyway). This allows short-running + // clients (like unit tests) to complete quickly. + result.setWorkerTimeout(0); + return result; + } + }); + case VM: + { + return getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker")); + } + default: + throw new AMQNoTransportForProtocolException(details, "Transport not recognised:" + transport, null); + } + } + + private static int getTransport(String transport) + { + if (transport.equals(BrokerDetails.SOCKET)) + { + return SOCKET; + } + + if (transport.equals(BrokerDetails.TCP)) + { + return TCP; + } + + if (transport.equals(BrokerDetails.VM)) + { + return VM; + } + + return -1; + } + + private static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate) + throws AMQVMBrokerCreationException + { + int port = details.getPort(); + + synchronized (_inVmPipeAddress) + { + if (!_inVmPipeAddress.containsKey(port)) + { + if (AutoCreate) + { + if (AutoCreate) + { + _logger.warn("Auto Creating InVM Broker on port:" + port); + createVMBroker(port); + } + else + { + throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port + + " does not exist. Auto create disabled.", null); + } + } + else + { + throw new AMQVMBrokerCreationException(null, port, "VM Broker on port " + port + + " does not exist. Auto create disabled.", null); + } + } + } + + return new VmPipeTransportConnection(port); + } + + public static void createVMBroker(int port) throws AMQVMBrokerCreationException + { + if (_acceptor == null) + { + _acceptor = new VmPipeAcceptor(); + + IoServiceConfig config = _acceptor.getDefaultConfig(); + + config.setThreadModel(ReadWriteThreadModel.getInstance()); + } + synchronized (_inVmPipeAddress) + { + + if (!_inVmPipeAddress.containsKey(port)) + { + _logger.info("Creating InVM Qpid.AMQP listening on port " + port); + IoHandlerAdapter provider = null; + try + { + VmPipeAddress pipe = new VmPipeAddress(port); + + provider = createBrokerInstance(port); + + _acceptor.bind(pipe, provider); + + _inVmPipeAddress.put(port, pipe); + _logger.info("Created InVM Qpid.AMQP listening on port " + port); + } + catch (IOException e) + { + _logger.error("Got IOException.", e); + + // Try and unbind provider + try + { + VmPipeAddress pipe = new VmPipeAddress(port); + + try + { + _acceptor.unbind(pipe); + } + catch (Exception ignore) + { + // ignore + } + + if (provider == null) + { + provider = createBrokerInstance(port); + } + + _acceptor.bind(pipe, provider); + _inVmPipeAddress.put(port, pipe); + _logger.info("Created InVM Qpid.AMQP listening on port " + port); + } + catch (IOException justUseFirstException) + { + String because; + if (e.getCause() == null) + { + because = e.toString(); + } + else + { + because = e.getCause().toString(); + } + + throw new AMQVMBrokerCreationException(null, port, because + " Stopped binding of InVM Qpid.AMQP", e); + } + } + + } + else + { + _logger.info("InVM Qpid.AMQP on port " + port + " already exits."); + } + } + } + + private static IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException + { + String protocolProviderClass = System.getProperty("amqj.protocolprovider.class", DEFAULT_QPID_SERVER); + _logger.info("Creating Qpid protocol provider: " + protocolProviderClass); + + // can't use introspection to get Provider as it is a server class. + // need to go straight to IoHandlerAdapter but that requries the queues and exchange from the ApplicationRegistry which we can't access. + + // get right constructor and pass in instancec ID - "port" + IoHandlerAdapter provider; + try + { + Class[] cnstr = {Integer.class}; + Object[] params = {port}; + provider = (IoHandlerAdapter) Class.forName(protocolProviderClass).getConstructor(cnstr).newInstance(params); + // Give the broker a second to create + _logger.info("Created VMBroker Instance:" + port); + } + catch (Exception e) + { + _logger.info("Unable to create InVM Qpid.AMQP on port " + port + ". Because: " + e.getCause()); + String because; + if (e.getCause() == null) + { + because = e.toString(); + } + else + { + because = e.getCause().toString(); + } + + AMQVMBrokerCreationException amqbce = + new AMQVMBrokerCreationException(null, port, because + " Stopped InVM Qpid.AMQP creation", e); + throw amqbce; + } + + return provider; + } + + public static void killAllVMBrokers() + { + _logger.info("Killing all VM Brokers"); + if (_acceptor != null) + { + _acceptor.unbindAll(); + } + synchronized (_inVmPipeAddress) + { + _inVmPipeAddress.clear(); + } + _acceptor = null; + _currentInstance = -1; + _currentVMPort = -1; + } + + public static void killVMBroker(int port) + { + synchronized (_inVmPipeAddress) + { + VmPipeAddress pipe = (VmPipeAddress) _inVmPipeAddress.get(port); + if (pipe != null) + { + _logger.info("Killing VM Broker:" + port); + _inVmPipeAddress.remove(port); + // This does need to be sychronized as otherwise mina can hang + // if a new connection is made + _acceptor.unbind(pipe); + } + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java new file mode 100644 index 0000000000..dca6efba67 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.transport; + +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.transport.vmpipe.QpidVmPipeConnector; +import org.apache.mina.transport.vmpipe.VmPipeAddress; +import org.apache.mina.transport.vmpipe.VmPipeConnector; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.pool.ReadWriteThreadModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class VmPipeTransportConnection implements ITransportConnection +{ + private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class); + + private static int _port; + + public VmPipeTransportConnection(int port) + { + _port = port; + } + + public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException + { + final VmPipeConnector ioConnector = new QpidVmPipeConnector(); + final IoServiceConfig cfg = ioConnector.getDefaultConfig(); + + cfg.setThreadModel(ReadWriteThreadModel.getInstance()); + + final VmPipeAddress address = new VmPipeAddress(_port); + _logger.info("Attempting connection to " + address); + ConnectFuture future = ioConnector.connect(address, protocolHandler); + // wait for connection to complete + future.join(); + // we call getSession which throws an IOException if there has been an error connecting + future.getSession(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java new file mode 100644 index 0000000000..fab95f754c --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser.java @@ -0,0 +1,244 @@ +package org.apache.qpid.client.url; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.StringTokenizer; + +import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.URLHelper; +import org.apache.qpid.url.URLSyntaxException; + +public class URLParser +{ + private AMQConnectionURL _url; + + public URLParser(AMQConnectionURL url)throws URLSyntaxException + { + _url = url; + parseURL(_url.getURL()); + } + + private void parseURL(String fullURL) throws URLSyntaxException + { + // Connection URL format + // amqp://[user:pass@][clientid]/virtualhost?brokerlist='tcp://host:port?option=\'value\',option=\'value\';vm://:3/virtualpath?option=\'value\'',failover='method?option=\'value\',option='value''" + // Options are of course optional except for requiring a single broker in the broker list. + try + { + URI connection = new URI(fullURL); + + if ((connection.getScheme() == null) || !(connection.getScheme().equalsIgnoreCase(AMQConnectionURL.AMQ_PROTOCOL))) + { + throw new URISyntaxException(fullURL, "Not an AMQP URL"); + } + + if ((connection.getHost() == null) || connection.getHost().equals("")) + { + String uid = AMQConnectionFactory.getUniqueClientID(); + if (uid == null) + { + throw URLHelper.parseError(-1, "Client Name not specified", fullURL); + } + else + { + _url.setClientName(uid); + } + + } + else + { + _url.setClientName(connection.getHost()); + } + + String userInfo = connection.getUserInfo(); + + if (userInfo == null) + { + // Fix for Java 1.5 which doesn't parse UserInfo for non http URIs + userInfo = connection.getAuthority(); + + if (userInfo != null) + { + int atIndex = userInfo.indexOf('@'); + + if (atIndex != -1) + { + userInfo = userInfo.substring(0, atIndex); + } + else + { + userInfo = null; + } + } + + } + + if (userInfo == null) + { + throw URLHelper.parseError(AMQConnectionURL.AMQ_PROTOCOL.length() + 3, "User information not found on url", fullURL); + } + else + { + parseUserInfo(userInfo); + } + + String virtualHost = connection.getPath(); + + if ((virtualHost != null) && (!virtualHost.equals(""))) + { + _url.setVirtualHost(virtualHost); + } + else + { + int authLength = connection.getAuthority().length(); + int start = AMQConnectionURL.AMQ_PROTOCOL.length() + 3; + int testIndex = start + authLength; + if ((testIndex < fullURL.length()) && (fullURL.charAt(testIndex) == '?')) + { + throw URLHelper.parseError(start, testIndex - start, "Virtual host found", fullURL); + } + else + { + throw URLHelper.parseError(-1, "Virtual host not specified", fullURL); + } + + } + + URLHelper.parseOptions(_url.getOptions(), connection.getQuery()); + + processOptions(); + } + catch (URISyntaxException uris) + { + if (uris instanceof URLSyntaxException) + { + throw (URLSyntaxException) uris; + } + + int slash = fullURL.indexOf("\\"); + + if (slash == -1) + { + throw URLHelper.parseError(uris.getIndex(), uris.getReason(), uris.getInput()); + } + else + { + if ((slash != 0) && (fullURL.charAt(slash - 1) == ':')) + { + throw URLHelper.parseError(slash - 2, fullURL.indexOf('?') - slash + 2, + "Virtual host looks like a windows path, forward slash not allowed in URL", fullURL); + } + else + { + throw URLHelper.parseError(slash, "Forward slash not allowed in URL", fullURL); + } + } + + } + } + + private void parseUserInfo(String userinfo) throws URLSyntaxException + { + // user info = user:pass + + int colonIndex = userinfo.indexOf(':'); + + if (colonIndex == -1) + { + throw URLHelper.parseError(AMQConnectionURL.AMQ_PROTOCOL.length() + 3, userinfo.length(), + "Null password in user information not allowed.", _url.getURL()); + } + else + { + _url.setUsername(userinfo.substring(0, colonIndex)); + _url.setPassword(userinfo.substring(colonIndex + 1)); + } + + } + + private void processOptions() throws URLSyntaxException + { + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_BROKERLIST)) + { + String brokerlist = _url.getOptions().get(AMQConnectionURL.OPTIONS_BROKERLIST); + + // brokerlist tcp://host:port?option='value',option='value';vm://:3/virtualpath?option='value' + StringTokenizer st = new StringTokenizer(brokerlist, "" + URLHelper.BROKER_SEPARATOR); + + while (st.hasMoreTokens()) + { + String broker = st.nextToken(); + + _url.addBrokerDetails(new AMQBrokerDetails(broker)); + } + + _url.getOptions().remove(AMQConnectionURL.OPTIONS_BROKERLIST); + } + + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_FAILOVER)) + { + String failover = _url.getOptions().get(AMQConnectionURL.OPTIONS_FAILOVER); + + // failover='method?option='value',option='value'' + + int methodIndex = failover.indexOf('?'); + + if (methodIndex > -1) + { + _url.setFailoverMethod(failover.substring(0, methodIndex)); + URLHelper.parseOptions(_url.getFailoverOptions(), failover.substring(methodIndex + 1)); + } + else + { + _url.setFailoverMethod(failover); + } + + _url.getOptions().remove(AMQConnectionURL.OPTIONS_FAILOVER); + } + + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_DEFAULT_TOPIC_EXCHANGE)) + { + _url.setDefaultTopicExchangeName(new AMQShortString(_url.getOptions().get(AMQConnectionURL.OPTIONS_DEFAULT_TOPIC_EXCHANGE))); + } + + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_DEFAULT_QUEUE_EXCHANGE)) + { + _url.setDefaultQueueExchangeName(new AMQShortString(_url.getOptions().get(AMQConnectionURL.OPTIONS_DEFAULT_QUEUE_EXCHANGE))); + } + + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_TEMPORARY_QUEUE_EXCHANGE)) + { + _url.setTemporaryQueueExchangeName(new AMQShortString(_url.getOptions().get(AMQConnectionURL.OPTIONS_TEMPORARY_QUEUE_EXCHANGE))); + } + + if (_url.getOptions().containsKey(AMQConnectionURL.OPTIONS_TEMPORARY_TOPIC_EXCHANGE)) + { + _url.setTemporaryTopicExchangeName(new AMQShortString(_url.getOptions().get(AMQConnectionURL.OPTIONS_TEMPORARY_TOPIC_EXCHANGE))); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser_0_10.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser_0_10.java new file mode 100644 index 0000000000..605e9ee154 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/url/URLParser_0_10.java @@ -0,0 +1,423 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.client.url; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.jms.BrokerDetails; + +/** + * The format Qpid URL is based on the AMQP one. + * The grammar is as follows: + *

    qpid_url = "qpid:" [client_props "@"] port_addr_list ["/" future-parameters] + *

    port_addr_list = [port_addr ","]* port_addr + *

    port_addr = tcp_port_addr | tls_prot_addr | future_prot_addr + *

    tcp_port_addr = tcp_id tcp_addr + *

    tcp_id = "tcp:" | "" + *

    tcp_addr = host [":" port] + *

    host = + *

    port = number + *

    tls_prot_addr = tls_id tls_addr + *

    tls_id = "tls:" | "" + *

    tls_addr = host [":" port] + *

    future_prot_addr = future_prot_id future_prot_addr + *

    future_prot_id = + *

    future_prot_addr = + *

    future_parameters = + *

    client_props = [client_prop ";"]* client_prop + *

    client_prop = prop "=" val + *

    prop = chars as per + *

    val = valid as per + *

    + * Ex: qpid:virtualhost=tcp:host-foo,test,client_id=foo@tcp:myhost.com:5672,virtualhost=prod; + * keystore=/opt/keystore@client_id2@tls:mysecurehost.com:5672 + */ +public class URLParser_0_10 +{ + private static final char[] URL_START_SEQ = new char[]{'q', 'p', 'i', 'd', ':'}; + private static final char PROPERTY_EQUALS_CHAR = '='; + private static final char PROPERTY_SEPARATOR_CHAR = ';'; + private static final char ADDRESS_SEPERATOR_CHAR = ','; + + //private static final char CLIENT_ID_TRANSPORT_SEPARATOR_CHAR = ':'; + private static final char TRANSPORT_HOST_SEPARATOR_CHAR = ':'; + private static final char HOST_PORT_SEPARATOR_CHAR = ':'; + private static final char AT_CHAR = '@'; + private static final char END_OF_URL_MARKER = '^'; + + enum URLParserState + { + QPID_URL_START, + ADDRESS_START, + PROPERTY_NAME, + PROPERTY_EQUALS, + PROPERTY_VALUE, + PROPERTY_SEPARATOR, + AT_CHAR, + TRANSPORT, + TRANSPORT_HOST_SEPARATOR, + HOST, + HOST_PORT_SEPARATOR, + PORT, + ADDRESS_END, + ADDRESS_SEPERATOR, + QPID_URL_END, + ERROR + } + + //-- Constructors + + private char[] _url; + private List _brokerDetailList = new ArrayList(); + private String _error; + private int _index = 0; + private BrokerDetails _currentBroker; + private String _currentPropName; + private boolean _endOfURL = false; + private URLParserState _currentParserState; + + public URLParser_0_10(String url) throws MalformedURLException + { + _url = (url + END_OF_URL_MARKER).toCharArray(); + _endOfURL = false; + _currentParserState = URLParserState.QPID_URL_START; + URLParserState prevState = _currentParserState; // for error handling + try + { + while (_currentParserState != URLParserState.ERROR && _currentParserState != URLParserState.QPID_URL_END) + { + prevState = _currentParserState; + _currentParserState = next(); + } + + if (_currentParserState == URLParserState.ERROR) + { + _error = + "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ") due to " + _error; + MalformedURLException ex; + ex = new MalformedURLException(_error); + throw ex; + } + } + catch (ArrayIndexOutOfBoundsException e) + { + _error = "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ")"; + MalformedURLException ex = new MalformedURLException(_error); + throw ex; + } + } + + //-- interface QpidURL + public List getAllBrokerDetails() + { + return _brokerDetailList; + } + + public String getURL() + { + return new String(_url); + } + + private URLParserState next() + { + switch (_currentParserState) + { + case QPID_URL_START: + return checkSequence(URL_START_SEQ, URLParserState.ADDRESS_START); + case ADDRESS_START: + return startAddress(); + case PROPERTY_NAME: + return extractPropertyName(); + case PROPERTY_EQUALS: + _index++; // skip the equal sign + return URLParserState.PROPERTY_VALUE; + case PROPERTY_VALUE: + return extractPropertyValue(); + case PROPERTY_SEPARATOR: + _index++; // skip "," + return URLParserState.PROPERTY_NAME; + case AT_CHAR: + _index++; // skip the @ sign + return URLParserState.TRANSPORT; + case TRANSPORT: + return extractTransport(); + case TRANSPORT_HOST_SEPARATOR: + _index++; // skip ":" + return URLParserState.HOST; + case HOST: + return extractHost(); + case HOST_PORT_SEPARATOR: + _index++; // skip ":" + return URLParserState.PORT; + case PORT: + return extractPort(); + case ADDRESS_END: + return endAddress(); + case ADDRESS_SEPERATOR: + _index++; // skip "," + return URLParserState.ADDRESS_START; + default: + return URLParserState.ERROR; + } + } + + private URLParserState checkSequence(char[] expected, URLParserState nextPart) + { + for (char expectedChar : expected) + { + if (expectedChar != _url[_index]) + { + _error = "Excepted (" + expectedChar + ") at position " + _index + ", got (" + _url[_index] + ")"; + return URLParserState.ERROR; + } + _index++; + } + return nextPart; + } + + private URLParserState startAddress() + { + _currentBroker = new AMQBrokerDetails(); + + for (int j = _index; j < _url.length; j++) + { + if (_url[j] == PROPERTY_EQUALS_CHAR) + { + return URLParserState.PROPERTY_NAME; + } + else if (_url[j] == ADDRESS_SEPERATOR_CHAR) + { + return URLParserState.TRANSPORT; + } + } + return URLParserState.TRANSPORT; + } + + private URLParserState endAddress() + { + _brokerDetailList.add(_currentBroker); + if (_endOfURL) + { + return URLParserState.QPID_URL_END; + } + else + { + return URLParserState.ADDRESS_SEPERATOR; + } + } + + private URLParserState extractPropertyName() + { + StringBuilder b = new StringBuilder(); + char next = _url[_index]; + while (next != PROPERTY_EQUALS_CHAR && next != AT_CHAR) + { + b.append(next); + next = _url[++_index]; + } + _currentPropName = b.toString(); + if (_currentPropName.trim().equals("")) + { + _error = "Property name cannot be empty"; + return URLParserState.ERROR; + } + else if (next == PROPERTY_EQUALS_CHAR) + { + return URLParserState.PROPERTY_EQUALS; + } + else + { + return URLParserState.AT_CHAR; + } + } + + private URLParserState extractPropertyValue() + { + StringBuilder b = new StringBuilder(); + char next = _url[_index]; + while (next != PROPERTY_SEPARATOR_CHAR && next != AT_CHAR) + { + b.append(next); + next = _url[++_index]; + } + String propValue = b.toString(); + if (propValue.trim().equals("")) + { + _error = "Property values cannot be empty"; + return URLParserState.ERROR; + } + else + { + _currentBroker.setProperty(_currentPropName, propValue); + if (next == PROPERTY_SEPARATOR_CHAR) + { + return URLParserState.PROPERTY_SEPARATOR; + } + else + { + return URLParserState.AT_CHAR; + } + } + } + + private URLParserState extractTransport() + { + String transport = buildUntil(TRANSPORT_HOST_SEPARATOR_CHAR); + if (transport.trim().equals("")) + { + _error = "Transport cannot be empty"; + return URLParserState.ERROR; + } + else if (!(transport.trim().equals(BrokerDetails.PROTOCOL_TCP) || transport.trim() + .equals(BrokerDetails.PROTOCOL_TLS))) + { + _error = "Transport cannot be " + transport + " value must be tcp or tls"; + return URLParserState.ERROR; + } + else + { + _currentBroker.setTransport(transport); + return URLParserState.TRANSPORT_HOST_SEPARATOR; + } + } + + private URLParserState extractHost() + { + char nextSep = 'c'; + String host; + URLParserState nextState; + + for (int i = _index; i < _url.length; i++) + { + if (_url[i] == HOST_PORT_SEPARATOR_CHAR) + { + nextSep = HOST_PORT_SEPARATOR_CHAR; + break; + } + else if (_url[i] == ADDRESS_SEPERATOR_CHAR) + { + nextSep = ADDRESS_SEPERATOR_CHAR; + break; + } + } + + if (nextSep == HOST_PORT_SEPARATOR_CHAR) + { + host = buildUntil(HOST_PORT_SEPARATOR_CHAR); + nextState = URLParserState.HOST_PORT_SEPARATOR; + } + else if (nextSep == ADDRESS_SEPERATOR_CHAR) + { + host = buildUntil(ADDRESS_SEPERATOR_CHAR); + nextState = URLParserState.ADDRESS_END; + } + else + { + host = buildUntil(END_OF_URL_MARKER); + nextState = URLParserState.ADDRESS_END; + _endOfURL = true; + } + + if (host.trim().equals("")) + { + _error = "Host cannot be empty"; + return URLParserState.ERROR; + } + else + { + _currentBroker.setHost(host); + return nextState; + } + } + + + private URLParserState extractPort() + { + + StringBuilder b = new StringBuilder(); + try + { + char next = _url[_index]; + while (next != ADDRESS_SEPERATOR_CHAR && next != END_OF_URL_MARKER ) + { + b.append(next); + next = _url[++_index]; + } + } + catch (ArrayIndexOutOfBoundsException e) + { + _endOfURL = true; + } + String portStr = b.toString(); + if (portStr.trim().equals("")) + { + _error = "Host cannot be empty"; + return URLParserState.ERROR; + } + else + { + try + { + int port = Integer.parseInt(portStr); + _currentBroker.setPort(port); + if( _url[_index] == END_OF_URL_MARKER ) + { + _endOfURL = true; + } + return URLParserState.ADDRESS_END; + } + catch (NumberFormatException e) + { + _error = "Illegal number for port"; + return URLParserState.ERROR; + } + } + } + + private String buildUntil(char c) + { + StringBuilder b = new StringBuilder(); + char next = _url[_index]; + while (next != c) + { + b.append(next); + next = _url[++_index]; + } + return b.toString(); + } + + public static void main(String[] args) + { + String testurl = "qpid:password=pass;username=name@tcp:test1"; + try + { + URLParser_0_10 impl = new URLParser_0_10(testurl); + for (BrokerDetails d : impl.getAllBrokerDetails()) + { + System.out.println(d); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java new file mode 100644 index 0000000000..67cda957fb --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/BlockingWaiter.java @@ -0,0 +1,348 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.util; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQTimeoutException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; + +/** + * BlockingWaiter is a 'rendezvous' which delegates handling of + * incoming Objects to a listener implemented as a sub-class of this and hands off the process or + * error to a consumer. The producer of the event does not have to wait for the consumer to take the event, so this + * differs from a 'rendezvous' in that sense. + * + *

    BlockingWaiters are used to coordinate when waiting for an an event that expect a response. + * They are always used in a 'one-shot' manner, that is, to recieve just one response. Usually the caller has to register + * them as method listeners with an event dispatcher and remember to de-register them (in a finally block) once they + * have been completed. + * + *

    The {@link #process} must return true on any incoming method that it handles. This indicates to + * this listeners that the object just processed ends the waiting process. + * + *

    Errors from the producer are rethrown to the consumer. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Accept generic objects as events for processing via {@link #process}. + *
    Delegate handling and undserstanding of the object to a concrete implementation. + *
    Block until {@link #process} determines that waiting is no longer required + *
    Propagate the most recent exception to the consumer. + *
    + * + * @todo Interuption is caught but not handled. This could be allowed to fall through. This might actually be usefull + * for fail-over where a thread is blocking when failure happens, it could be interrupted to abandon or retry + * when this happens. At the very least, restore the interrupted status flag. + * @todo If the retrotranslator can handle it, could use a SynchronousQueue to implement this rendezvous. Need to + * check that SynchronousQueue has a non-blocking put method available. + */ +public abstract class BlockingWaiter +{ + /** This flag is used to indicate that the blocked for method has been received. */ + private volatile boolean _ready = false; + + /** This flag is used to indicate that the received error has been processed. */ + private volatile boolean _errorAck = false; + + /** Used to protect the shared event and ready flag between the producer and consumer. */ + private final ReentrantLock _lock = new ReentrantLock(); + + /** Used to signal that a method has been received */ + private final Condition _receivedCondition = _lock.newCondition(); + + /** Used to signal that a error has been processed */ + private final Condition _errorConditionAck = _lock.newCondition(); + + /** Used to hold the most recent exception that is passed to the {@link #error(Exception)} method. */ + private volatile Exception _error; + + /** Holds the incomming Object. */ + protected Object _doneObject = null; + private AtomicBoolean _waiting = new AtomicBoolean(false); + private boolean _closed = false; + + /** + * Delegates processing of the incomming object to the handler. + * + * @param object The object to process. + * + * @return true if the waiting is complete, false if waiting should continue. + */ + public abstract boolean process(T object); + + /** + * An Object has been received and should be processed to see if our wait condition has been reached. + * + * @param object The object received. + * + * @return true if the waiting is complete, false if waiting should continue. + */ + public boolean received(T object) + { + + boolean ready = process(object); + + if (ready) + { + // we only update the flag from inside the synchronized block + // so that the blockForFrame method cannot "miss" an update - it + // will only ever read the flag from within the synchronized block + _lock.lock(); + try + { + _doneObject = object; + _ready = ready; + _receivedCondition.signal(); + } + finally + { + _lock.unlock(); + } + } + + return ready; + } + + /** + * Blocks until an object is received that is handled by process, or the specified timeout + * has passed. + * + * Once closed any attempt to wait will throw an exception. + * + * @param timeout The timeout in milliseconds. + * + * @return The object that resolved the blocking. + * + * @throws AMQException + * @throws FailoverException + */ + public Object block(long timeout) throws AMQException, FailoverException + { + long nanoTimeout = TimeUnit.MILLISECONDS.toNanos(timeout); + + _lock.lock(); + + try + { + if (_closed) + { + throw throwClosedException(); + } + + if (_error == null) + { + _waiting.set(true); + + while (!_ready) + { + try + { + if (timeout == -1) + { + _receivedCondition.await(); + } + else + { + nanoTimeout = _receivedCondition.awaitNanos(nanoTimeout); + + if (nanoTimeout <= 0 && !_ready && _error == null) + { + _error = new AMQTimeoutException("Server did not respond in a timely fashion", null); + _ready = true; + } + } + } + catch (InterruptedException e) + { + System.err.println(e.getMessage()); + // IGNORE -- //fixme this isn't ideal as being interrupted isn't equivellant to sucess + // if (!_ready && timeout != -1) + // { + // _error = new AMQException("Server did not respond timely"); + // _ready = true; + // } + } + } + } + + if (_error != null) + { + if (_error instanceof AMQException) + { + throw (AMQException) _error; + } + else if (_error instanceof FailoverException) + { + // This should ensure that FailoverException is not wrapped and can be caught. + throw (FailoverException) _error; // needed to expose FailoverException. + } + else + { + throw new AMQException("Woken up due to " + _error.getClass(), _error); + } + } + + } + finally + { + _waiting.set(false); + + //Release Error handling thread + if (_error != null) + { + _errorAck = true; + _errorConditionAck.signal(); + + _error = null; + } + _lock.unlock(); + } + + return _doneObject; + } + + /** + * This is a callback, called when an error has occured that should interupt any waiter. + * It is also called from within this class to avoid code repetition but it should only be called by the MINA threads. + * + * Once closed any notification of an exception will be ignored. + * + * @param e The exception being propogated. + */ + public void error(Exception e) + { + // set the error so that the thread that is blocking (against blockForFrame()) + // can pick up the exception and rethrow to the caller + + _lock.lock(); + + if (_closed) + { + return; + } + + if (_error == null) + { + _error = e; + } + else + { + System.err.println("WARNING: new error arrived while old one not yet processed"); + } + + try + { + if (_waiting.get()) + { + + _ready = true; + _receivedCondition.signal(); + + while (!_errorAck) + { + try + { + _errorConditionAck.await(); + } + catch (InterruptedException e1) + { + System.err.println(e.getMessage()); + } + } + _errorAck = false; + } + } + finally + { + _lock.unlock(); + } + } + + /** + * Close this Waiter so that no more errors are processed. + * This is a preventative method to ensure that a second error thread does not get stuck in the error method after + * the await has returned. This has not happend but in practise but if two errors occur on the Connection at + * the same time then it is conceiveably possible for the second to get stuck if the first one is processed by a + * waiter. + * + * Once closed any attempt to wait will throw an exception. + * Any notification of an exception will be ignored. + */ + public void close() + { + _lock.lock(); + try + { + //if we have already closed then our job is done. + if (_closed) + { + return; + } + + //Close Waiter so no more exceptions are processed + _closed = true; + + //Wake up any await() threads + + //If we are waiting then use the error() to wake them up. + if (_waiting.get()) + { + error(throwClosedException()); + } + //If they are not waiting then there is nothing to do. + + // Wake up any error handling threads + + if (!_errorAck) + { + _errorAck = true; + _errorConditionAck.signal(); + + _error = null; + } + } + finally + { + _lock.unlock(); + } + } + + /** + * Helper method to generate the a closed Exception. + * + * todo: This should be converted to something more friendly. + * + * @return AMQException to throw to waiters when the Waiter is closed. + */ + private AMQException throwClosedException() + { + return new AMQException(null, "Waiter was closed.", null); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java new file mode 100644 index 0000000000..bddbc329ab --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java @@ -0,0 +1,125 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.util; + +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow + * control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the + * caller is not obliged to react to the events.

    This implementation is only safe where we have a single + * thread adding items and a single (different) thread removing items. + * + * @todo Make this implement java.util.Queue and hide the implementation. Then different queue types can be substituted. + */ +public class FlowControllingBlockingQueue +{ + /** This queue is bounded and is used to store messages before being dispatched to the consumer */ + private final Queue _queue = new ConcurrentLinkedQueue(); + + private final int _flowControlHighThreshold; + private final int _flowControlLowThreshold; + + private final ThresholdListener _listener; + + /** We require a separate count so we can track whether we have reached the threshold */ + private int _count; + + public boolean isEmpty() + { + return _queue.isEmpty(); + } + + public interface ThresholdListener + { + void aboveThreshold(int currentValue); + + void underThreshold(int currentValue); + } + + public FlowControllingBlockingQueue(int threshold, ThresholdListener listener) + { + this(threshold, threshold, listener); + } + + public FlowControllingBlockingQueue(int highThreshold, int lowThreshold, ThresholdListener listener) + { + _flowControlHighThreshold = highThreshold; + _flowControlLowThreshold = lowThreshold; + _listener = listener; + } + + public Object take() throws InterruptedException + { + Object o = _queue.poll(); + if(o == null) + { + synchronized(this) + { + while((o = _queue.poll())==null) + { + wait(); + } + } + } + if (_listener != null) + { + synchronized (_listener) + { + if (_count-- == _flowControlLowThreshold) + { + _listener.underThreshold(_count); + } + } + } + + return o; + } + + public void add(Object o) + { + synchronized(this) + { + _queue.add(o); + + notifyAll(); + } + if (_listener != null) + { + synchronized (_listener) + { + if (++_count == _flowControlHighThreshold) + { + _listener.aboveThreshold(_count); + } + } + } + } + + public Iterator iterator() + { + return _queue.iterator(); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java new file mode 100644 index 0000000000..dc0d9b8c78 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.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.client.vmbroker; + +import org.apache.qpid.client.transport.AMQTransportConnectionException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQVMBrokerCreationException represents failure to create an in VM broker on the vm transport medium. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represent failure to create an in VM broker. + *
    + * + * @todo Error code never used. This is not an AMQException. + */ +public class AMQVMBrokerCreationException extends AMQTransportConnectionException +{ + private int _port; + + /** + * @param port + * + * @deprecated + */ + public AMQVMBrokerCreationException(int port) + { + this(null, port, "Unable to create vm broker", null); + } + + public AMQVMBrokerCreationException(AMQConstant errorCode, int port, String message, Throwable cause) + { + super(errorCode, message, cause); + _port = port; + } + + public String toString() + { + return super.toString() + " on port " + _port; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ArithmeticExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ArithmeticExpression.java new file mode 100644 index 0000000000..56d1bdcdc5 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ArithmeticExpression.java @@ -0,0 +1,268 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +/** + * An expression which performs an operation on two expression values + */ +public abstract class ArithmeticExpression extends BinaryExpression +{ + + protected static final int INTEGER = 1; + protected static final int LONG = 2; + protected static final int DOUBLE = 3; + + + public ArithmeticExpression(Expression left, Expression right) + { + super(left, right); + } + + public static Expression createPlus(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof String) + { + String text = (String) lvalue; + return text + rvalue; + } + else if (lvalue instanceof Number) + { + return plus((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call plus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "+"; + } + }; + } + + public static Expression createMinus(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return minus((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call minus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "-"; + } + }; + } + + public static Expression createMultiply(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return multiply((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call multiply operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "*"; + } + }; + } + + public static Expression createDivide(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return divide((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call divide operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "/"; + } + }; + } + + public static Expression createMod(Expression left, Expression right) + { + return new ArithmeticExpression(left, right) + { + + protected Object evaluate(Object lvalue, Object rvalue) + { + if (lvalue instanceof Number) + { + return mod((Number) lvalue, asNumber(rvalue)); + } + + throw new RuntimeException("Cannot call mod operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() + { + return "%"; + } + }; + } + + protected Number plus(Number left, Number right) + { + switch (numberType(left, right)) + { + + case ArithmeticExpression.INTEGER: + return new Integer(left.intValue() + right.intValue()); + + case ArithmeticExpression.LONG: + return new Long(left.longValue() + right.longValue()); + + default: + return new Double(left.doubleValue() + right.doubleValue()); + } + } + + protected Number minus(Number left, Number right) + { + switch (numberType(left, right)) + { + + case ArithmeticExpression.INTEGER: + return new Integer(left.intValue() - right.intValue()); + + case ArithmeticExpression.LONG: + return new Long(left.longValue() - right.longValue()); + + default: + return new Double(left.doubleValue() - right.doubleValue()); + } + } + + protected Number multiply(Number left, Number right) + { + switch (numberType(left, right)) + { + + case ArithmeticExpression.INTEGER: + return new Integer(left.intValue() * right.intValue()); + + case ArithmeticExpression.LONG: + return new Long(left.longValue() * right.longValue()); + + default: + return new Double(left.doubleValue() * right.doubleValue()); + } + } + + protected Number divide(Number left, Number right) + { + return new Double(left.doubleValue() / right.doubleValue()); + } + + protected Number mod(Number left, Number right) + { + return new Double(left.doubleValue() % right.doubleValue()); + } + + private int numberType(Number left, Number right) + { + if (isDouble(left) || isDouble(right)) + { + return ArithmeticExpression.DOUBLE; + } + else if ((left instanceof Long) || (right instanceof Long)) + { + return ArithmeticExpression.LONG; + } + else + { + return ArithmeticExpression.INTEGER; + } + } + + private boolean isDouble(Number n) + { + return (n instanceof Float) || (n instanceof Double); + } + + protected Number asNumber(Object value) + { + if (value instanceof Number) + { + return (Number) value; + } + else + { + throw new RuntimeException("Cannot convert value: " + value + " into a number"); + } + } + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Object lvalue = left.evaluate(message); + if (lvalue == null) + { + return null; + } + + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + return evaluate(lvalue, rvalue); + } + + /** + * @param lvalue + * @param rvalue + * @return + */ + protected abstract Object evaluate(Object lvalue, Object rvalue); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BinaryExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BinaryExpression.java new file mode 100644 index 0000000000..f97f858fad --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BinaryExpression.java @@ -0,0 +1,103 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +/** + * An expression which performs an operation on two expression values. + */ +public abstract class BinaryExpression implements Expression +{ + protected Expression left; + protected Expression right; + + public BinaryExpression(Expression left, Expression right) + { + this.left = left; + this.right = right; + } + + public Expression getLeft() + { + return left; + } + + public Expression getRight() + { + return right; + } + + /** + * @see Object#toString() + */ + public String toString() + { + return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#equals(Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + public abstract String getExpressionSymbol(); + + /** + * @param expression + */ + public void setRight(Expression expression) + { + right = expression; + } + + /** + * @param expression + */ + public void setLeft(Expression expression) + { + left = expression; + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BooleanExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BooleanExpression.java new file mode 100644 index 0000000000..cc24c81729 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/BooleanExpression.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.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +/** + * A BooleanExpression is an expression that always + * produces a Boolean result. + */ +public interface BooleanExpression extends Expression +{ + + public boolean matches(AbstractJMSMessage message) throws QpidException; + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ComparisonExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ComparisonExpression.java new file mode 100644 index 0000000000..adf360698b --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ComparisonExpression.java @@ -0,0 +1,589 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + +import java.util.HashSet; +import java.util.List; +import java.util.regex.Pattern; + +/** + * A filter performing a comparison of two objects + */ +public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression +{ + + public static BooleanExpression createBetween(Expression value, Expression left, Expression right) + { + return LogicExpression.createAND(ComparisonExpression.createGreaterThanEqual(value, left), ComparisonExpression.createLessThanEqual(value, right)); + } + + public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) + { + return LogicExpression.createOR(ComparisonExpression.createLessThan(value, left), ComparisonExpression.createGreaterThan(value, right)); + } + + private static final HashSet REGEXP_CONTROL_CHARS = new HashSet(); + + static + { + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('.')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('\\')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('[')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character(']')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('^')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('$')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('?')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('*')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('+')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('{')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('}')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('|')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('(')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character(')')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character(':')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('&')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('<')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('>')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('=')); + ComparisonExpression.REGEXP_CONTROL_CHARS.add(new Character('!')); + } + + static class LikeExpression extends UnaryExpression implements BooleanExpression + { + + Pattern likePattern; + + /** + * @param right + */ + public LikeExpression(Expression right, String like, int escape) + { + super(right); + + StringBuffer regexp = new StringBuffer(like.length() * 2); + regexp.append("\\A"); // The beginning of the input + for (int i = 0; i < like.length(); i++) + { + char c = like.charAt(i); + if (escape == (0xFFFF & c)) + { + i++; + if (i >= like.length()) + { + // nothing left to escape... + break; + } + + char t = like.charAt(i); + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & t)); + } + else if (c == '%') + { + regexp.append(".*?"); // Do a non-greedy match + } + else if (c == '_') + { + regexp.append("."); // match one + } + else if (ComparisonExpression.REGEXP_CONTROL_CHARS.contains(new Character(c))) + { + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & c)); + } + else + { + regexp.append(c); + } + } + + regexp.append("\\z"); // The end of the input + + likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL); + } + + /** + * org.apache.activemq.filter.UnaryExpression#getExpressionSymbol() + */ + public String getExpressionSymbol() + { + return "LIKE"; + } + + /** + * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext) + */ + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + + Object rv = this.getRight().evaluate(message); + + if (rv == null) + { + return null; + } + + if (!(rv instanceof String)) + { + return + Boolean.FALSE; + // throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass()); + } + + return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; + } + + public boolean matches(AbstractJMSMessage message) throws QpidException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + + public static BooleanExpression createLike(Expression left, String right, String escape) + { + if ((escape != null) && (escape.length() != 1)) + { + throw new RuntimeException( + "The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape); + } + + int c = -1; + if (escape != null) + { + c = 0xFFFF & escape.charAt(0); + } + + return new LikeExpression(left, right, c); + } + + public static BooleanExpression createNotLike(Expression left, String right, String escape) + { + return UnaryExpression.createNOT(ComparisonExpression.createLike(left, right, escape)); + } + + public static BooleanExpression createInFilter(Expression left, List elements) + { + + if (!(left instanceof PropertyExpression)) + { + throw new RuntimeException("Expected a property for In expression, got: " + left); + } + + return UnaryExpression.createInExpression((PropertyExpression) left, elements, false); + + } + + public static BooleanExpression createNotInFilter(Expression left, List elements) + { + + if (!(left instanceof PropertyExpression)) + { + throw new RuntimeException("Expected a property for In expression, got: " + left); + } + + return UnaryExpression.createInExpression((PropertyExpression) left, elements, true); + + } + + public static BooleanExpression createIsNull(Expression left) + { + return ComparisonExpression.doCreateEqual(left, ConstantExpression.NULL); + } + + public static BooleanExpression createIsNotNull(Expression left) + { + return UnaryExpression.createNOT(ComparisonExpression.doCreateEqual(left, ConstantExpression.NULL)); + } + + public static BooleanExpression createNotEqual(Expression left, Expression right) + { + return UnaryExpression.createNOT(ComparisonExpression.createEqual(left, right)); + } + + public static BooleanExpression createEqual(Expression left, Expression right) + { + ComparisonExpression.checkEqualOperand(left); + ComparisonExpression.checkEqualOperand(right); + ComparisonExpression.checkEqualOperandCompatability(left, right); + + return ComparisonExpression.doCreateEqual(left, right); + } + + private static BooleanExpression doCreateEqual(Expression left, Expression right) + { + return new ComparisonExpression(left, right) + { + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Object lv = left.evaluate(message); + Object rv = right.evaluate(message); + + // Iff one of the values is null + if ((lv == null) ^ (rv == null)) + { + return Boolean.FALSE; + } + + if ((lv == rv) || lv.equals(rv)) + { + return Boolean.TRUE; + } + + if ((lv instanceof Comparable) && (rv instanceof Comparable)) + { + return compare((Comparable) lv, (Comparable) rv); + } + + return Boolean.FALSE; + } + + protected boolean asBoolean(int answer) + { + return answer == 0; + } + + public String getExpressionSymbol() + { + return "="; + } + }; + } + + public static BooleanExpression createGreaterThan(final Expression left, final Expression right) + { + ComparisonExpression.checkLessThanOperand(left); + ComparisonExpression.checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + protected boolean asBoolean(int answer) + { + return answer > 0; + } + + public String getExpressionSymbol() + { + return ">"; + } + }; + } + + public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) + { + ComparisonExpression.checkLessThanOperand(left); + ComparisonExpression.checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + protected boolean asBoolean(int answer) + { + return answer >= 0; + } + + public String getExpressionSymbol() + { + return ">="; + } + }; + } + + public static BooleanExpression createLessThan(final Expression left, final Expression right) + { + ComparisonExpression.checkLessThanOperand(left); + ComparisonExpression.checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + + protected boolean asBoolean(int answer) + { + return answer < 0; + } + + public String getExpressionSymbol() + { + return "<"; + } + + }; + } + + public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) + { + ComparisonExpression.checkLessThanOperand(left); + ComparisonExpression.checkLessThanOperand(right); + + return new ComparisonExpression(left, right) + { + + protected boolean asBoolean(int answer) + { + return answer <= 0; + } + + public String getExpressionSymbol() + { + return "<="; + } + }; + } + + /** + * Only Numeric expressions can be used in >, >=, < or <= expressions.s + * + * @param expr + */ + public static void checkLessThanOperand(Expression expr) + { + if (expr instanceof ConstantExpression) + { + Object value = ((ConstantExpression) expr).getValue(); + if (value instanceof Number) + { + return; + } + + // Else it's boolean or a String.. + throw new RuntimeException("Value '" + expr + "' cannot be compared."); + } + + if (expr instanceof BooleanExpression) + { + throw new RuntimeException("Value '" + expr + "' cannot be compared."); + } + } + + /** + * Validates that the expression can be used in == or <> expression. + * Cannot not be NULL TRUE or FALSE litterals. + * + * @param expr + */ + public static void checkEqualOperand(Expression expr) + { + if (expr instanceof ConstantExpression) + { + Object value = ((ConstantExpression) expr).getValue(); + if (value == null) + { + throw new RuntimeException("'" + expr + "' cannot be compared."); + } + } + } + + /** + * + * @param left + * @param right + */ + private static void checkEqualOperandCompatability(Expression left, Expression right) + { + if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression)) + { + if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression)) + { + throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'"); + } + } + } + + /** + * @param left + * @param right + */ + public ComparisonExpression(Expression left, Expression right) + { + super(left, right); + } + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Comparable lv = (Comparable) left.evaluate(message); + if (lv == null) + { + return null; + } + + Comparable rv = (Comparable) right.evaluate(message); + if (rv == null) + { + return null; + } + + return compare(lv, rv); + } + + protected Boolean compare(Comparable lv, Comparable rv) + { + Class lc = lv.getClass(); + Class rc = rv.getClass(); + // If the the objects are not of the same type, + // try to convert up to allow the comparison. + if (lc != rc) + { + if (lc == Byte.class) + { + if (rc == Short.class) + { + lv = new Short(((Number) lv).shortValue()); + } + else if (rc == Integer.class) + { + lv = new Integer(((Number) lv).intValue()); + } + else if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Short.class) + { + if (rc == Integer.class) + { + lv = new Integer(((Number) lv).intValue()); + } + else if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Integer.class) + { + if (rc == Long.class) + { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Long.class) + { + if (rc == Integer.class) + { + rv = new Long(((Number) rv).longValue()); + } + else if (rc == Float.class) + { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Float.class) + { + if (rc == Integer.class) + { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Long.class) + { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Double.class) + { + lv = new Double(((Number) lv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else if (lc == Double.class) + { + if (rc == Integer.class) + { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Long.class) + { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Float.class) + { + rv = new Float(((Number) rv).doubleValue()); + } + else + { + return Boolean.FALSE; + } + } + else + { + return Boolean.FALSE; + } + } + + return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE; + } + + protected abstract boolean asBoolean(int answer); + + public boolean matches(AbstractJMSMessage message) throws QpidException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ConstantExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ConstantExpression.java new file mode 100644 index 0000000000..447de914a4 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/ConstantExpression.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.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + +import java.math.BigDecimal; + +/** + * Represents a constant expression + */ +public class ConstantExpression implements Expression +{ + + static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression + { + public BooleanConstantExpression(Object value) + { + super(value); + } + + public boolean matches(AbstractJMSMessage message) throws QpidException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + + public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null); + public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE); + public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE); + + private Object value; + + public static ConstantExpression createFromDecimal(String text) + { + + // Strip off the 'l' or 'L' if needed. + if (text.endsWith("l") || text.endsWith("L")) + { + text = text.substring(0, text.length() - 1); + } + + Number value; + try + { + value = new Long(text); + } + catch (NumberFormatException e) + { + // The number may be too big to fit in a long. + value = new BigDecimal(text); + } + + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFromHex(String text) + { + Number value = new Long(Long.parseLong(text.substring(2), 16)); + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFromOctal(String text) + { + Number value = new Long(Long.parseLong(text, 8)); + long l = value.longValue(); + if ((Integer.MIN_VALUE <= l) && (l <= Integer.MAX_VALUE)) + { + value = new Integer(value.intValue()); + } + + return new ConstantExpression(value); + } + + public static ConstantExpression createFloat(String text) + { + Number value = new Double(text); + + return new ConstantExpression(value); + } + + public ConstantExpression(Object value) + { + this.value = value; + } + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + return value; + } + + public Object getValue() + { + return value; + } + + /** + * @see Object#toString() + */ + public String toString() + { + if (value == null) + { + return "NULL"; + } + + if (value instanceof Boolean) + { + return ((Boolean) value).booleanValue() ? "TRUE" : "FALSE"; + } + + if (value instanceof String) + { + return ConstantExpression.encodeString((String) value); + } + + return value.toString(); + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#equals(Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Encodes the value of string so that it looks like it would look like + * when it was provided in a selector. + * + * @param s + * @return + */ + public static String encodeString(String s) + { + StringBuffer b = new StringBuffer(); + b.append('\''); + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + if (c == '\'') + { + b.append(c); + } + + b.append(c); + } + + b.append('\''); + + return b.toString(); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/Expression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/Expression.java new file mode 100644 index 0000000000..e578775a77 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/Expression.java @@ -0,0 +1,34 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +/** + * Represents an expression + */ +public interface Expression +{ + /** + * @param message The message to evaluate + * @return the value of this expression + */ + public Object evaluate(AbstractJMSMessage message) throws QpidException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java new file mode 100644 index 0000000000..dcfb9a9940 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/JMSSelectorFilter.java @@ -0,0 +1,70 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.QpidException; +import org.apache.qpid.filter.selector.SelectorParser; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +public class JMSSelectorFilter implements MessageFilter +{ + /** + * this JMSSelectorFilter's logger + */ + private static final Logger _logger = LoggerFactory.getLogger(JMSSelectorFilter.class); + + private String _selector; + private BooleanExpression _matcher; + + public JMSSelectorFilter(String selector) throws QpidException + { + _selector = selector; + if (JMSSelectorFilter._logger.isDebugEnabled()) + { + JMSSelectorFilter._logger.debug("Created JMSSelectorFilter with selector:" + _selector); + } + _matcher = new SelectorParser().parse(selector); + } + + public boolean matches(AbstractJMSMessage message) + { + try + { + boolean match = _matcher.matches(message); + if (JMSSelectorFilter._logger.isDebugEnabled()) + { + JMSSelectorFilter._logger.debug(message + " match(" + match + ") selector(" + System + .identityHashCode(_selector) + "):" + _selector); + } + return match; + } + catch (QpidException e) + { + JMSSelectorFilter._logger.warn("Caght exception when evaluating message selector for message " + message, e); + } + return false; + } + + public String getSelector() + { + return _selector; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/LogicExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/LogicExpression.java new file mode 100644 index 0000000000..d7aabd5a46 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/LogicExpression.java @@ -0,0 +1,108 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +/** + * A filter performing a comparison of two objects + */ +public abstract class LogicExpression extends BinaryExpression implements BooleanExpression +{ + + public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) + { + return new LogicExpression(lvalue, rvalue) + { + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + + Boolean lv = (Boolean) left.evaluate(message); + // Can we do an OR shortcut?? + if ((lv != null) && lv.booleanValue()) + { + return Boolean.TRUE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "OR"; + } + }; + } + + public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) + { + return new LogicExpression(lvalue, rvalue) + { + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + + Boolean lv = (Boolean) left.evaluate(message); + + // Can we do an AND shortcut?? + if (lv == null) + { + return null; + } + + if (!lv.booleanValue()) + { + return Boolean.FALSE; + } + + Boolean rv = (Boolean) right.evaluate(message); + + return (rv == null) ? null : rv; + } + + public String getExpressionSymbol() + { + return "AND"; + } + }; + } + + /** + * @param left + * @param right + */ + public LogicExpression(BooleanExpression left, BooleanExpression right) + { + super(left, right); + } + + public abstract Object evaluate(AbstractJMSMessage message) throws QpidException; + + public boolean matches(AbstractJMSMessage message) throws QpidException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/MessageFilter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/MessageFilter.java new file mode 100644 index 0000000000..a775080d81 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/MessageFilter.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.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + + +public interface MessageFilter +{ + boolean matches(AbstractJMSMessage message) throws QpidException; +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java new file mode 100644 index 0000000000..2c05f5ce0f --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/PropertyExpression.java @@ -0,0 +1,279 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.QpidException; +import org.apache.qpid.ErrorCode; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import javax.jms.JMSException; +import java.util.HashMap; + +/** + * Represents a property expression + */ +public class PropertyExpression implements Expression +{ + // Constants - defined the same as JMS + private static final int NON_PERSISTENT = 1; + private static final int DEFAULT_PRIORITY = 4; + + private static final Logger _logger = LoggerFactory.getLogger(PropertyExpression.class); + + private static final HashMap JMS_PROPERTY_EXPRESSIONS = new HashMap(); + + static + { + JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + //TODO + return null; + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + return message.getReplyToString(); + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + return message.getJMSType(); + } + catch (JMSException e) + { + _logger.warn("Error evaluating property", e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + int mode = message.getJMSDeliveryMode(); + if (_logger.isDebugEnabled()) + { + _logger.debug("JMSDeliveryMode is :" + mode); + } + + return mode; + } + catch (JMSException e) + { + _logger.warn("Error evaluating property",e); + } + + return NON_PERSISTENT; + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + return message.getJMSPriority(); + } + catch (Exception e) + { + _logger.warn("Error evaluating property",e); + } + + return DEFAULT_PRIORITY; + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + + try + { + return message.getJMSMessageID(); + } + catch (JMSException e) + { + _logger.warn("Error evaluating property",e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + return message.getJMSTimestamp(); + } + catch (Exception e) + { + _logger.warn("Error evaluating property",e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + + try + { + return message.getJMSCorrelationID(); + } + catch (JMSException e) + { + _logger.warn("Error evaluating property",e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + + try + { + return message.getJMSExpiration(); + } + catch (JMSException e) + { + _logger.warn("Error evaluating property",e); + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression() + { + public Object evaluate(AbstractJMSMessage message) + { + try + { + return message.getJMSRedelivered(); + } + catch (JMSException e) + { + _logger.warn("Error evaluating property",e); + return null; + } + } + }); + + } + + private final String name; + private final Expression jmsPropertyExpression; + + public PropertyExpression(String name) + { + this.name = name; + jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name); + } + + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + + if (jmsPropertyExpression != null) + { + return jmsPropertyExpression.evaluate(message); + } + else + { + + try + { + + if (_logger.isDebugEnabled()) + { + _logger.debug("Looking up property:" + name); + _logger.debug("Properties are:" + message.getPropertyNames()); + } + return message.getObjectProperty(name); + } + catch(JMSException e) + { + throw new QpidException("Exception evaluating properties for filter", ErrorCode.INTERNAL_ERROR, e); + } + } + } + + public String getName() + { + return name; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() + { + return name; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) + { + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + return name.equals(((PropertyExpression) o).name); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/UnaryExpression.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/UnaryExpression.java new file mode 100644 index 0000000000..b620b107c4 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/filter/UnaryExpression.java @@ -0,0 +1,321 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.filter; + +import org.apache.qpid.QpidException; +import org.apache.qpid.client.message.AbstractJMSMessage; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +/** + * An expression which performs an operation on two expression values + */ +public abstract class UnaryExpression implements Expression +{ + + private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE); + protected Expression right; + + public static Expression createNegate(Expression left) + { + return new UnaryExpression(left) + { + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue instanceof Number) + { + return UnaryExpression.negate((Number) rvalue); + } + + return null; + } + + public String getExpressionSymbol() + { + return "-"; + } + }; + } + + public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not) + { + + // Use a HashSet if there are many elements. + Collection t; + if (elements.size() == 0) + { + t = null; + } + else if (elements.size() < 5) + { + t = elements; + } + else + { + t = new HashSet(elements); + } + + final Collection inList = t; + + return new BooleanUnaryExpression(right) + { + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue.getClass() != String.class) + { + return null; + } + + if (((inList != null) && inList.contains(rvalue)) ^ not) + { + return Boolean.TRUE; + } + else + { + return Boolean.FALSE; + } + + } + + public String toString() + { + StringBuffer answer = new StringBuffer(); + answer.append(right); + answer.append(" "); + answer.append(getExpressionSymbol()); + answer.append(" ( "); + + int count = 0; + for (Iterator i = inList.iterator(); i.hasNext();) + { + Object o = (Object) i.next(); + if (count != 0) + { + answer.append(", "); + } + + answer.append(o); + count++; + } + + answer.append(" )"); + + return answer.toString(); + } + + public String getExpressionSymbol() + { + if (not) + { + return "NOT IN"; + } + else + { + return "IN"; + } + } + }; + } + + abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression + { + public BooleanUnaryExpression(Expression left) + { + super(left); + } + + public boolean matches(AbstractJMSMessage message) throws QpidException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + + ; + + public static BooleanExpression createNOT(BooleanExpression left) + { + return new BooleanUnaryExpression(left) + { + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Boolean lvalue = (Boolean) right.evaluate(message); + if (lvalue == null) + { + return null; + } + + return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + public String getExpressionSymbol() + { + return "NOT"; + } + }; + } + public static BooleanExpression createBooleanCast(Expression left) + { + return new BooleanUnaryExpression(left) + { + public Object evaluate(AbstractJMSMessage message) throws QpidException + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (!rvalue.getClass().equals(Boolean.class)) + { + return Boolean.FALSE; + } + + return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE; + } + + public String toString() + { + return right.toString(); + } + + public String getExpressionSymbol() + { + return ""; + } + }; + } + + private static Number negate(Number left) + { + Class clazz = left.getClass(); + if (clazz == Integer.class) + { + return new Integer(-left.intValue()); + } + else if (clazz == Long.class) + { + return new Long(-left.longValue()); + } + else if (clazz == Float.class) + { + return new Float(-left.floatValue()); + } + else if (clazz == Double.class) + { + return new Double(-left.doubleValue()); + } + else if (clazz == BigDecimal.class) + { + // We ussually get a big deciamal when we have Long.MIN_VALUE constant in the + // Selector. Long.MIN_VALUE is too big to store in a Long as a positive so we store it + // as a Big decimal. But it gets Negated right away.. to here we try to covert it back + // to a Long. + BigDecimal bd = (BigDecimal) left; + bd = bd.negate(); + + if (UnaryExpression.BD_LONG_MIN_VALUE.compareTo(bd) == 0) + { + return new Long(Long.MIN_VALUE); + } + + return bd; + } + else + { + throw new RuntimeException("Don't know how to negate: " + left); + } + } + + public UnaryExpression(Expression left) + { + this.right = left; + } + + public Expression getRight() + { + return right; + } + + public void setRight(Expression expression) + { + right = expression; + } + + /** + * @see Object#toString() + */ + public String toString() + { + return "(" + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#hashCode() + */ + public int hashCode() + { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see Object#equals(Object) + */ + public boolean equals(Object o) + { + + if ((o == null) || !this.getClass().equals(o.getClass())) + { + return false; + } + + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + public abstract String getExpressionSymbol(); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java new file mode 100644 index 0000000000..07e1be95dc --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java @@ -0,0 +1,103 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jms; + +import java.util.Map; + +import org.apache.qpid.client.SSLConfiguration; + +public interface BrokerDetails +{ + + /* + * Known URL Options + * @see ConnectionURL + */ + public static final String OPTIONS_RETRY = "retries"; + public static final String OPTIONS_CONNECT_TIMEOUT = "connecttimeout"; + public static final String OPTIONS_CONNECT_DELAY = "connectdelay"; + public static final int DEFAULT_PORT = 5672; + + public static final String SOCKET = "socket"; + public static final String TCP = "tcp"; + public static final String VM = "vm"; + + public static final String DEFAULT_TRANSPORT = TCP; + + public static final String URL_FORMAT_EXAMPLE = + "://[:][?

    + * Based on class from ActiveMQ. + */ +public class NameParserImpl implements NameParser +{ + public Name parse(String name) throws NamingException + { + return new CompositeName(name); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java new file mode 100644 index 0000000000..43ac56dee9 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java @@ -0,0 +1,359 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jndi; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Queue; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQHeadersExchange; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertiesFileInitialContextFactory implements InitialContextFactory +{ + protected final Logger _logger = LoggerFactory.getLogger(PropertiesFileInitialContextFactory.class); + + private String CONNECTION_FACTORY_PREFIX = "connectionfactory."; + private String DESTINATION_PREFIX = "destination."; + private String QUEUE_PREFIX = "queue."; + private String TOPIC_PREFIX = "topic."; + + public Context getInitialContext(Hashtable environment) throws NamingException + { + Map data = new ConcurrentHashMap(); + + try + { + + String file = null; + if (environment.containsKey(Context.PROVIDER_URL)) + { + file = (String) environment.get(Context.PROVIDER_URL); + } + else + { + file = System.getProperty(Context.PROVIDER_URL); + } + + if (file != null) + { + _logger.info("Loading Properties from:" + file); + // Load the properties specified + Properties p = new Properties(); + + p.load(new BufferedInputStream(new FileInputStream(file))); + + environment.putAll(p); + System.getProperties().putAll(p); + _logger.info("Loaded Context Properties:" + environment.toString()); + } + else + { + _logger.info("No Provider URL specified."); + } + } + catch (IOException ioe) + { + _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL)); + } + + createConnectionFactories(data, environment); + + createDestinations(data, environment); + + createQueues(data, environment); + + createTopics(data, environment); + + return createContext(data, environment); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected ReadOnlyContext createContext(Map data, Hashtable environment) + { + return new ReadOnlyContext(environment, data); + } + + protected void createConnectionFactories(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(CONNECTION_FACTORY_PREFIX)) + { + String jndiName = key.substring(CONNECTION_FACTORY_PREFIX.length()); + ConnectionFactory cf = createFactory(entry.getValue().toString()); + if (cf != null) + { + data.put(jndiName, cf); + } + } + } + } + + protected void createDestinations(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(DESTINATION_PREFIX)) + { + String jndiName = key.substring(DESTINATION_PREFIX.length()); + Destination dest = createDestination(entry.getValue().toString()); + if (dest != null) + { + data.put(jndiName, dest); + } + } + } + } + + protected void createQueues(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(QUEUE_PREFIX)) + { + String jndiName = key.substring(QUEUE_PREFIX.length()); + Queue q = createQueue(entry.getValue().toString()); + if (q != null) + { + data.put(jndiName, q); + } + } + } + } + + protected void createTopics(Map data, Hashtable environment) + { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) + { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(TOPIC_PREFIX)) + { + String jndiName = key.substring(TOPIC_PREFIX.length()); + Topic t = createTopic(entry.getValue().toString()); + if (t != null) + { + if (_logger.isDebugEnabled()) + { + StringBuffer b = new StringBuffer(); + b.append("Creating the topic: " + jndiName + " with the following binding keys "); + for (AMQShortString binding:((AMQTopic)t).getBindingKeys()) + { + b.append(binding.toString()).append(","); + } + + _logger.debug(b.toString()); + } + data.put(jndiName, t); + } + } + } + } + + /** + * Factory method to create new Connection Factory instances + */ + protected ConnectionFactory createFactory(String url) + { + try + { + return new AMQConnectionFactory(url); + } + catch (URLSyntaxException urlse) + { + _logger.warn("Unable to createFactories:" + urlse); + } + + return null; + } + + /** + * Factory method to create new Destination instances from an AMQP BindingURL + */ + protected Destination createDestination(String bindingURL) + { + AMQBindingURL binding; + try + { + binding = new AMQBindingURL(bindingURL); + } + catch (URISyntaxException urlse) + { + _logger.warn("Unable to create destination:" + urlse, urlse); + + return null; + } + + try + { + return AMQDestination.createDestination(binding); + } + catch (IllegalArgumentException iaw) + { + _logger.warn("Binding: '" + binding + "' not supported"); + + return null; + } + } + + /** + * Factory method to create new Queue instances + */ + protected Queue createQueue(Object value) + { + if (value instanceof AMQShortString) + { + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, (AMQShortString) value); + } + else if (value instanceof String) + { + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString((String) value)); + } + else if (value instanceof BindingURL) + { + return new AMQQueue((BindingURL) value); + } + + return null; + } + + /** + * Factory method to create new Topic instances + */ + protected Topic createTopic(Object value) + { + if (value instanceof AMQShortString) + { + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, (AMQShortString) value); + } + else if (value instanceof String) + { + String[] keys = ((String)value).split(","); + AMQShortString[] bindings = new AMQShortString[keys.length]; + int i = 0; + for (String key:keys) + { + bindings[i] = new AMQShortString(key); + i++; + } + // The Destination has a dual nature. If this was used for a producer the key is used + // for the routing key. If it was used for the consumer it becomes the bindingKey + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME,bindings[0],null,bindings); + } + else if (value instanceof BindingURL) + { + return new AMQTopic((BindingURL) value); + } + + return null; + } + + /** + * Factory method to create new HeaderExcahnge instances + */ + protected Destination createHeaderExchange(Object value) + { + if (value instanceof String) + { + return new AMQHeadersExchange((String) value); + } + else if (value instanceof BindingURL) + { + return new AMQHeadersExchange((BindingURL) value); + } + + return null; + } + + // Properties + // ------------------------------------------------------------------------- + public String getConnectionPrefix() + { + return CONNECTION_FACTORY_PREFIX; + } + + public void setConnectionPrefix(String connectionPrefix) + { + this.CONNECTION_FACTORY_PREFIX = connectionPrefix; + } + + public String getDestinationPrefix() + { + return DESTINATION_PREFIX; + } + + public void setDestinationPrefix(String destinationPrefix) + { + this.DESTINATION_PREFIX = destinationPrefix; + } + + public String getQueuePrefix() + { + return QUEUE_PREFIX; + } + + public void setQueuePrefix(String queuePrefix) + { + this.QUEUE_PREFIX = queuePrefix; + } + + public String getTopicPrefix() + { + return TOPIC_PREFIX; + } + + public void setTopicPrefix(String topicPrefix) + { + this.TOPIC_PREFIX = topicPrefix; + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java new file mode 100644 index 0000000000..1719ea1219 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java @@ -0,0 +1,527 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jndi; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +import javax.naming.Binding; +import javax.naming.CompositeName; +import javax.naming.Context; +import javax.naming.LinkRef; +import javax.naming.Name; +import javax.naming.NameClassPair; +import javax.naming.NameNotFoundException; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.NotContextException; +import javax.naming.OperationNotSupportedException; +import javax.naming.Reference; +import javax.naming.spi.NamingManager; + +/** + * Based on class from ActiveMQ. + * A read-only Context + *

    + * This version assumes it and all its subcontext are read-only and any attempt + * to modify (e.g. through bind) will result in an OperationNotSupportedException. + * Each Context in the tree builds a cache of the entries in all sub-contexts + * to optimise the performance of lookup. + *

    + *

    This implementation is intended to optimise the performance of lookup(String) + * to about the level of a HashMap get. It has been observed that the scheme + * resolution phase performed by the JVM takes considerably longer, so for + * optimum performance lookups should be coded like:

    + * + * Context componentContext = (Context)new InitialContext().lookup("java:comp"); + * String envEntry = (String) componentContext.lookup("env/myEntry"); + * String envEntry2 = (String) componentContext.lookup("env/myEntry2"); + * + */ +public class ReadOnlyContext implements Context, Serializable +{ + private static final long serialVersionUID = -5754338187296859149L; + protected static final NameParser nameParser = new NameParserImpl(); + + protected final Hashtable environment; // environment for this context + protected final Map bindings; // bindings at my level + protected final Map treeBindings; // all bindings under me + + private boolean frozen = false; + private String nameInNamespace = ""; + public static final String SEPARATOR = "/"; + + public ReadOnlyContext() + { + environment = new Hashtable(); + bindings = new HashMap(); + treeBindings = new HashMap(); + } + + public ReadOnlyContext(Hashtable env) + { + if (env == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(env); + } + + this.bindings = Collections.EMPTY_MAP; + this.treeBindings = Collections.EMPTY_MAP; + } + + public ReadOnlyContext(Hashtable environment, Map bindings) + { + if (environment == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(environment); + } + + this.bindings = bindings; + treeBindings = new HashMap(); + frozen = true; + } + + public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) + { + this(environment, bindings); + this.nameInNamespace = nameInNamespace; + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) + { + this.bindings = clone.bindings; + this.treeBindings = clone.treeBindings; + this.environment = new Hashtable(env); + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) + { + this(clone, env); + this.nameInNamespace = nameInNamespace; + } + + public void freeze() + { + frozen = true; + } + + boolean isFrozen() + { + return frozen; + } + + /** + * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses. + * It binds every possible lookup into a map in each context. To do this, each context + * strips off one name segment and if necessary creates a new context for it. Then it asks that context + * to bind the remaining name. It returns a map containing all the bindings from the next context, plus + * the context it just created (if it in fact created it). (the names are suitably extended by the segment + * originally lopped off). + * + * @param name + * @param value + * @return + * @throws javax.naming.NamingException + */ + protected Map internalBind(String name, Object value) throws NamingException + { + assert (name != null) && (name.length() > 0); + assert !frozen; + + Map newBindings = new HashMap(); + int pos = name.indexOf('/'); + if (pos == -1) + { + if (treeBindings.put(name, value) != null) + { + throw new NamingException("Something already bound at " + name); + } + + bindings.put(name, value); + newBindings.put(name, value); + } + else + { + String segment = name.substring(0, pos); + assert segment != null; + assert !segment.equals(""); + Object o = treeBindings.get(segment); + if (o == null) + { + o = newContext(); + treeBindings.put(segment, o); + bindings.put(segment, o); + newBindings.put(segment, o); + } + else if (!(o instanceof ReadOnlyContext)) + { + throw new NamingException("Something already bound where a subcontext should go"); + } + + ReadOnlyContext readOnlyContext = (ReadOnlyContext) o; + String remainder = name.substring(pos + 1); + Map subBindings = readOnlyContext.internalBind(remainder, value); + for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry entry = (Map.Entry) iterator.next(); + String subName = segment + "/" + (String) entry.getKey(); + Object bound = entry.getValue(); + treeBindings.put(subName, bound); + newBindings.put(subName, bound); + } + } + + return newBindings; + } + + protected ReadOnlyContext newContext() + { + return new ReadOnlyContext(); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException + { + return environment.put(propName, propVal); + } + + public Hashtable getEnvironment() throws NamingException + { + return (Hashtable) environment.clone(); + } + + public Object removeFromEnvironment(String propName) throws NamingException + { + return environment.remove(propName); + } + + public Object lookup(String name) throws NamingException + { + if (name.length() == 0) + { + return this; + } + + Object result = treeBindings.get(name); + if (result == null) + { + result = bindings.get(name); + } + + if (result == null) + { + int pos = name.indexOf(':'); + if (pos > 0) + { + String scheme = name.substring(0, pos); + Context ctx = NamingManager.getURLContext(scheme, environment); + if (ctx == null) + { + throw new NamingException("scheme " + scheme + " not recognized"); + } + + return ctx.lookup(name); + } + else + { + // Split out the first name of the path + // and look for it in the bindings map. + CompositeName path = new CompositeName(name); + + if (path.size() == 0) + { + return this; + } + else + { + String first = path.get(0); + Object obj = bindings.get(first); + if (obj == null) + { + throw new NameNotFoundException(name); + } + else if ((obj instanceof Context) && (path.size() > 1)) + { + Context subContext = (Context) obj; + obj = subContext.lookup(path.getSuffix(1)); + } + + return obj; + } + } + } + + if (result instanceof LinkRef) + { + LinkRef ref = (LinkRef) result; + result = lookup(ref.getLinkName()); + } + + if (result instanceof Reference) + { + try + { + result = NamingManager.getObjectInstance(result, null, null, this.environment); + } + catch (NamingException e) + { + throw e; + } + catch (Exception e) + { + throw (NamingException) new NamingException("could not look up : " + name).initCause(e); + } + } + + if (result instanceof ReadOnlyContext) + { + String prefix = getNameInNamespace(); + if (prefix.length() > 0) + { + prefix = prefix + SEPARATOR; + } + + result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name); + } + + return result; + } + + public Object lookup(Name name) throws NamingException + { + return lookup(name.toString()); + } + + public Object lookupLink(String name) throws NamingException + { + return lookup(name); + } + + public Name composeName(Name name, Name prefix) throws NamingException + { + Name result = (Name) prefix.clone(); + result.addAll(name); + + return result; + } + + public String composeName(String name, String prefix) throws NamingException + { + CompositeName result = new CompositeName(prefix); + result.addAll(new CompositeName(name)); + + return result.toString(); + } + + public NamingEnumeration list(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ListEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).list(""); + } + else + { + throw new NotContextException(); + } + } + + public NamingEnumeration listBindings(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ListBindingEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).listBindings(""); + } + else + { + throw new NotContextException(); + } + } + + public Object lookupLink(Name name) throws NamingException + { + return lookupLink(name.toString()); + } + + public NamingEnumeration list(Name name) throws NamingException + { + return list(name.toString()); + } + + public NamingEnumeration listBindings(Name name) throws NamingException + { + return listBindings(name.toString()); + } + + public void bind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void bind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void close() throws NamingException + { + // ignore + } + + public Context createSubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public String getNameInNamespace() throws NamingException + { + return nameInNamespace; + } + + public NameParser getNameParser(Name name) throws NamingException + { + return nameParser; + } + + public NameParser getNameParser(String name) throws NamingException + { + return nameParser; + } + + public void rebind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(String oldName, String newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + private abstract class LocalNamingEnumeration implements NamingEnumeration + { + private Iterator i = bindings.entrySet().iterator(); + + public boolean hasMore() throws NamingException + { + return i.hasNext(); + } + + public boolean hasMoreElements() + { + return i.hasNext(); + } + + protected Map.Entry getNext() + { + return (Map.Entry) i.next(); + } + + public void close() throws NamingException + { } + } + + private class ListEnumeration extends LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); + } + } + + private class ListBindingEnumeration extends LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new Binding((String) entry.getKey(), entry.getValue()); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/ReadOnlyContext.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/ReadOnlyContext.java new file mode 100644 index 0000000000..59ec4cfba7 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/ReadOnlyContext.java @@ -0,0 +1,509 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.naming; + +import org.apache.qpid.jndi.NameParserImpl; + +import javax.naming.*; +import javax.naming.spi.NamingManager; +import java.io.Serializable; +import java.util.*; + +/** + * Based on class from ActiveMQ. + * A read-only Context + *

    + * This version assumes it and all its subcontext are read-only and any attempt + * to modify (e.g. through bind) will result in an OperationNotSupportedException. + * Each Context in the tree builds a cache of the entries in all sub-contexts + * to optimise the performance of lookup. + *

    + *

    This implementation is intended to optimise the performance of lookup(String) + * to about the level of a HashMap get. It has been observed that the scheme + * resolution phase performed by the JVM takes considerably longer, so for + * optimum performance lookups should be coded like:

    + * + * Context componentContext = (Context)new InitialContext().lookup("java:comp"); + * String envEntry = (String) componentContext.lookup("env/myEntry"); + * String envEntry2 = (String) componentContext.lookup("env/myEntry2"); + * + */ +public class ReadOnlyContext implements Context, Serializable +{ + private static final long serialVersionUID = -5754338187296859149L; + protected static final NameParser nameParser = new NameParserImpl(); + + protected final Hashtable environment; // environment for this context + protected final Map bindings; // bindings at my level + protected final Map treeBindings; // all bindings under me + + private boolean frozen = false; + private String nameInNamespace = ""; + public static final String SEPARATOR = "/"; + + public ReadOnlyContext() + { + environment = new Hashtable(); + bindings = new HashMap(); + treeBindings = new HashMap(); + } + + public ReadOnlyContext(Hashtable env) + { + if (env == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(env); + } + + this.bindings = Collections.EMPTY_MAP; + this.treeBindings = Collections.EMPTY_MAP; + } + + public ReadOnlyContext(Hashtable environment, Map bindings) + { + if (environment == null) + { + this.environment = new Hashtable(); + } + else + { + this.environment = new Hashtable(environment); + } + + this.bindings = bindings; + treeBindings = new HashMap(); + frozen = true; + } + + public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) + { + this(environment, bindings); + this.nameInNamespace = nameInNamespace; + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) + { + this.bindings = clone.bindings; + this.treeBindings = clone.treeBindings; + this.environment = new Hashtable(env); + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) + { + this(clone, env); + this.nameInNamespace = nameInNamespace; + } + + public void freeze() + { + frozen = true; + } + + boolean isFrozen() + { + return frozen; + } + + /** + * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses. + * It binds every possible lookup into a map in each context. To do this, each context + * strips off one name segment and if necessary creates a new context for it. Then it asks that context + * to bind the remaining name. It returns a map containing all the bindings from the next context, plus + * the context it just created (if it in fact created it). (the names are suitably extended by the segment + * originally lopped off). + * + * @param name + * @param value + * @return + * @throws javax.naming.NamingException + */ + protected Map internalBind(String name, Object value) throws NamingException + { + assert (name != null) && (name.length() > 0); + assert !frozen; + + Map newBindings = new HashMap(); + int pos = name.indexOf('/'); + if (pos == -1) + { + if (treeBindings.put(name, value) != null) + { + throw new NamingException("Something already bound at " + name); + } + + bindings.put(name, value); + newBindings.put(name, value); + } + else + { + String segment = name.substring(0, pos); + assert segment != null; + assert !segment.equals(""); + Object o = treeBindings.get(segment); + if (o == null) + { + o = newContext(); + treeBindings.put(segment, o); + bindings.put(segment, o); + newBindings.put(segment, o); + } + else if (!(o instanceof ReadOnlyContext)) + { + throw new NamingException("Something already bound where a subcontext should go"); + } + + ReadOnlyContext readOnlyContext = (ReadOnlyContext) o; + String remainder = name.substring(pos + 1); + Map subBindings = readOnlyContext.internalBind(remainder, value); + for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) + { + Map.Entry entry = (Map.Entry) iterator.next(); + String subName = segment + "/" + (String) entry.getKey(); + Object bound = entry.getValue(); + treeBindings.put(subName, bound); + newBindings.put(subName, bound); + } + } + + return newBindings; + } + + protected ReadOnlyContext newContext() + { + return new ReadOnlyContext(); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException + { + return environment.put(propName, propVal); + } + + public Hashtable getEnvironment() throws NamingException + { + return (Hashtable) environment.clone(); + } + + public Object removeFromEnvironment(String propName) throws NamingException + { + return environment.remove(propName); + } + + public Object lookup(String name) throws NamingException + { + if (name.length() == 0) + { + return this; + } + + Object result = treeBindings.get(name); + if (result == null) + { + result = bindings.get(name); + } + + if (result == null) + { + int pos = name.indexOf(':'); + if (pos > 0) + { + String scheme = name.substring(0, pos); + Context ctx = NamingManager.getURLContext(scheme, environment); + if (ctx == null) + { + throw new NamingException("scheme " + scheme + " not recognized"); + } + + return ctx.lookup(name); + } + else + { + // Split out the first name of the path + // and look for it in the bindings map. + CompositeName path = new CompositeName(name); + + if (path.size() == 0) + { + return this; + } + else + { + String first = path.get(0); + Object obj = bindings.get(first); + if (obj == null) + { + throw new NameNotFoundException(name); + } + else if ((obj instanceof Context) && (path.size() > 1)) + { + Context subContext = (Context) obj; + obj = subContext.lookup(path.getSuffix(1)); + } + + return obj; + } + } + } + + if (result instanceof LinkRef) + { + LinkRef ref = (LinkRef) result; + result = lookup(ref.getLinkName()); + } + + if (result instanceof Reference) + { + try + { + result = NamingManager.getObjectInstance(result, null, null, this.environment); + } + catch (NamingException e) + { + throw e; + } + catch (Exception e) + { + throw (NamingException) new NamingException("could not look up : " + name).initCause(e); + } + } + + if (result instanceof ReadOnlyContext) + { + String prefix = getNameInNamespace(); + if (prefix.length() > 0) + { + prefix = prefix + SEPARATOR; + } + + result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name); + } + + return result; + } + + public Object lookup(Name name) throws NamingException + { + return lookup(name.toString()); + } + + public Object lookupLink(String name) throws NamingException + { + return lookup(name); + } + + public Name composeName(Name name, Name prefix) throws NamingException + { + Name result = (Name) prefix.clone(); + result.addAll(name); + + return result; + } + + public String composeName(String name, String prefix) throws NamingException + { + CompositeName result = new CompositeName(prefix); + result.addAll(new CompositeName(name)); + + return result.toString(); + } + + public NamingEnumeration list(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ReadOnlyContext.ListEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).list(""); + } + else + { + throw new NotContextException(); + } + } + + public NamingEnumeration listBindings(String name) throws NamingException + { + Object o = lookup(name); + if (o == this) + { + return new ReadOnlyContext.ListBindingEnumeration(); + } + else if (o instanceof Context) + { + return ((Context) o).listBindings(""); + } + else + { + throw new NotContextException(); + } + } + + public Object lookupLink(Name name) throws NamingException + { + return lookupLink(name.toString()); + } + + public NamingEnumeration list(Name name) throws NamingException + { + return list(name.toString()); + } + + public NamingEnumeration listBindings(Name name) throws NamingException + { + return listBindings(name.toString()); + } + + public void bind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void bind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void close() throws NamingException + { + // ignore + } + + public Context createSubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public String getNameInNamespace() throws NamingException + { + return nameInNamespace; + } + + public NameParser getNameParser(Name name) throws NamingException + { + return nameParser; + } + + public NameParser getNameParser(String name) throws NamingException + { + return nameParser; + } + + public void rebind(Name name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void rename(String oldName, String newName) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(Name name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + public void unbind(String name) throws NamingException + { + throw new OperationNotSupportedException(); + } + + private abstract class LocalNamingEnumeration implements NamingEnumeration + { + private Iterator i = bindings.entrySet().iterator(); + + public boolean hasMore() throws NamingException + { + return i.hasNext(); + } + + public boolean hasMoreElements() + { + return i.hasNext(); + } + + protected Map.Entry getNext() + { + return (Map.Entry) i.next(); + } + + public void close() throws NamingException + { } + } + + private class ListEnumeration extends ReadOnlyContext.LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); + } + } + + private class ListBindingEnumeration extends ReadOnlyContext.LocalNamingEnumeration + { + public Object next() throws NamingException + { + return nextElement(); + } + + public Object nextElement() + { + Map.Entry entry = getNext(); + + return new Binding((String) entry.getKey(), entry.getValue()); + } + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/jndi.properties b/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/jndi.properties new file mode 100644 index 0000000000..830de5f619 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/naming/jndi.properties @@ -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. +# +java.naming.factory.initial = org.apache.qpid.naming.PropertiesFileInitialConextFactory + +# use the following property to configure the default connector +#java.naming.provider.url - ignored. + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +# qpid:username=foo;password=password;client_id=id;virtualhost=path@tpc:localhost:1556 +connectionfactory.local = qpid:tcp:localhost' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.ibmStocks = stocks.nyse.ibm + +# Register an AMQP destination in JNDI +# NOTE: Qpid currently only supports direct,topics and headers +# destination.[jniName] = [BindingURL] +destination.direct = direct://amq.direct//directQueue diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java new file mode 100644 index 0000000000..e19058881e --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/JMSTestCase.java @@ -0,0 +1,135 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.nclient; + +import java.util.Enumeration; + +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.QueueBrowser; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.framing.AMQShortString; + +public class JMSTestCase +{ + + public static void main(String[] args) + { + + try + { + javax.jms.Connection con = new AMQConnection("qpid:password=pass;username=name@tcp:localhost:5672"); + con.start(); + + javax.jms.Session ssn = con.createSession(false, 1); + + javax.jms.Destination dest = new AMQQueue(new AMQShortString("direct"),"test"); + javax.jms.MessageProducer prod = ssn.createProducer(dest); + QueueBrowser browser = ssn.createBrowser((Queue)dest, "Test = 'test'"); + + javax.jms.TextMessage msg = ssn.createTextMessage(); + msg.setStringProperty("TEST", "test"); + msg.setText("Should get this"); + prod.send(msg); + + javax.jms.TextMessage msg2 = ssn.createTextMessage(); + msg2.setStringProperty("TEST", "test2"); + msg2.setText("Shouldn't get this"); + prod.send(msg2); + + + Enumeration enu = browser.getEnumeration(); + for (;enu.hasMoreElements();) + { + System.out.println(enu.nextElement()); + System.out.println("\n"); + } + + javax.jms.MessageConsumer cons = ssn.createConsumer(dest, "Test = 'test'"); + javax.jms.TextMessage m = null; // (javax.jms.TextMessage)cons.receive(); + cons.setMessageListener(new MessageListener() + { + public void onMessage(Message m) + { + javax.jms.TextMessage m2 = (javax.jms.TextMessage)m; + try + { + System.out.println("headers : " + m2.toString()); + System.out.println("m : " + m2.getText()); + System.out.println("\n\n"); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + }); + + con.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException e) + { + e.printStackTrace(); + } + }); + + System.out.println("Waiting"); + while (m == null) + { + + } + + System.out.println("Exiting"); + + /*javax.jms.TextMessage msg = ssn.createTextMessage(); + msg.setText("This is a test message"); + msg.setBooleanProperty("targetMessage", false); + prod.send(msg); + + msg.setBooleanProperty("targetMessage", true); + prod.send(msg); + + javax.jms.TextMessage m = (javax.jms.TextMessage)cons.receiveNoWait(); + + if (m == null) + { + System.out.println("message is null"); + } + else + { + System.out.println("message is not null" + m); + }*/ + + } + catch(Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/MessagePartListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/MessagePartListener.java new file mode 100644 index 0000000000..6f07dcb469 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/MessagePartListener.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.nclient; + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Header; +import org.apache.qpid.transport.MessageTransfer; + +/** + * Assembles message parts. + *

    The sequence of event for transferring a message is as follows: + *

      + *
    • messageHeaders + *
    • n calls to addData + *
    • messageReceived + *
    + * It is up to the implementation to assemble the message once the different parts + * are transferred. + */ +public interface MessagePartListener +{ + + /** + * Inform the listener of the message transfer + * + * @param xfr the message transfer object + */ + public void messageTransfer(MessageTransfer xfr); + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java new file mode 100644 index 0000000000..14bfb4f95e --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/ByteBufferMessage.java @@ -0,0 +1,190 @@ +package org.apache.qpid.nclient.util; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.*; + +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.MessageProperties; +import org.apache.qpid.transport.Header; +import org.apache.qpid.api.Message; + +/** + *

    A Simple implementation of the message interface + * for small messages. When the readData methods are called + * we assume the message is complete. i.e there want be any + * appendData operations after that.

    + * + *

    If you need large message support please see + * FileMessage and StreamingMessage + *

    + */ +public class ByteBufferMessage implements Message +{ + private List _data;// = new ArrayList(); + private ByteBuffer _readBuffer; + private int _dataSize; + private DeliveryProperties _currentDeliveryProps; + private MessageProperties _currentMessageProps; + private int _transferId; + private Header _header; + + public ByteBufferMessage(MessageProperties messageProperties, DeliveryProperties deliveryProperties) + { + _currentMessageProps = messageProperties; + _currentDeliveryProps = deliveryProperties; + } + + public void setHeader(Header header) { + _header = header; + } + + public Header getHeader() { + return _header; + } + + public ByteBufferMessage() + { + _currentDeliveryProps = new DeliveryProperties(); + _currentMessageProps = new MessageProperties(); + } + + public ByteBufferMessage(int transferId) + { + _transferId = transferId; + } + + public int getMessageTransferId() + { + return _transferId; + } + + public void clearData() + { + _data = new LinkedList(); + _readBuffer = null; + } + + public void appendData(byte[] src) throws IOException + { + appendData(ByteBuffer.wrap(src)); + } + + /** + * write the data from the current position up to the buffer limit + */ + public void appendData(ByteBuffer src) throws IOException + { + if(_data == null) + { + _data = Collections.singletonList(src); + } + else + { + if(_data.size() == 1) + { + _data = new ArrayList(_data); + } + _data.add(src); + } + _dataSize += src.remaining(); + } + + public DeliveryProperties getDeliveryProperties() + { + return _currentDeliveryProps; + } + + public MessageProperties getMessageProperties() + { + return _currentMessageProps; + } + + public void setDeliveryProperties(DeliveryProperties props) + { + _currentDeliveryProps = props; + } + + public void setMessageProperties(MessageProperties props) + { + _currentMessageProps = props; + } + + public void readData(byte[] target) + { + getReadBuffer().get(target); + } + + public ByteBuffer readData() + { + return getReadBuffer(); + } + + private void buildReadBuffer() + { + //optimize for the simple cases + if(_data.size() == 1) + { + _readBuffer = _data.get(0).duplicate(); + } + else + { + _readBuffer = ByteBuffer.allocate(_dataSize); + for(ByteBuffer buf:_data) + { + _readBuffer.put(buf); + } + _readBuffer.flip(); + } + } + + private ByteBuffer getReadBuffer() + { + if (_readBuffer != null ) + { + return _readBuffer.slice(); + } + else + { + if (_data.size() >0) + { + buildReadBuffer(); + return _readBuffer.slice(); + } + else + { + return ByteBuffer.allocate(0); + } + } + } + + //hack for testing + @Override public String toString() + { + ByteBuffer temp = getReadBuffer(); + byte[] b = new byte[temp.remaining()]; + temp.get(b); + return new String(b); + } +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessageListener.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessageListener.java new file mode 100644 index 0000000000..c5edd62143 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessageListener.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.nclient.util; + +import org.apache.qpid.api.Message; + +/** + *A message listener + */ +public interface MessageListener +{ + /** + * Process an incoming message. + * + * @param message The incoming message. + */ + public void onMessage(Message message); +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java new file mode 100644 index 0000000000..10fd8d2a80 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/nclient/util/MessagePartListenerAdapter.java @@ -0,0 +1,88 @@ +package org.apache.qpid.nclient.util; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.*; +import org.apache.qpid.nclient.MessagePartListener; + +/** + * This is a simple message assembler. + * Will call onMessage method of the adaptee + * when all message data is read. + * + * This is a good convinience utility for handling + * small messages + */ +public class MessagePartListenerAdapter implements MessagePartListener +{ + MessageListener _adaptee; + ByteBufferMessage _currentMsg; + + public MessagePartListenerAdapter(MessageListener listener) + { + _adaptee = listener; + } + + public void messageTransfer(MessageTransfer xfr) + { + _currentMsg = new ByteBufferMessage(xfr.getId()); + + for (Struct st : xfr.getHeader().getStructs()) + { + if(st instanceof DeliveryProperties) + { + _currentMsg.setDeliveryProperties((DeliveryProperties)st); + + } + else if(st instanceof MessageProperties) + { + _currentMsg.setMessageProperties((MessageProperties)st); + } + + } + + + ByteBuffer body = xfr.getBody(); + if (body == null) + { + body = ByteBuffer.allocate(0); + } + + + try + { + _currentMsg.appendData(body); + } + catch(IOException e) + { + // A chance for IO exception + // doesn't occur as we are using + // a ByteBuffer + } + + _adaptee.onMessage(_currentMsg); + } + +} diff --git a/RC6/qpid/java/client/src/main/java/org/apache/qpid/njms/ExceptionHelper.java b/RC6/qpid/java/client/src/main/java/org/apache/qpid/njms/ExceptionHelper.java new file mode 100644 index 0000000000..ce790a3b24 --- /dev/null +++ b/RC6/qpid/java/client/src/main/java/org/apache/qpid/njms/ExceptionHelper.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.njms; + +import org.apache.qpid.QpidException; + +import javax.jms.JMSException; +import javax.transaction.xa.XAException; + +/** + * Helper class for handling exceptions + */ +public class ExceptionHelper +{ + static public JMSException convertQpidExceptionToJMSException(Exception exception) + { + JMSException jmsException = null; + if (!(exception instanceof JMSException)) + { + if (exception instanceof QpidException) + { + jmsException = new JMSException(exception.getMessage(), String.valueOf(((QpidException) exception).getErrorCode())); + } + else + { + jmsException = new JMSException(exception.getMessage()); + } + jmsException.setLinkedException(exception); + jmsException.initCause(exception); + } + else + { + jmsException = (JMSException) exception; + } + return jmsException; + } + + static public XAException convertQpidExceptionToXAException(QpidException exception) + { + String qpidErrorCode = String.valueOf(exception.getErrorCode()); + // todo map this error to an XA code + int xaCode = XAException.XAER_PROTO; + return new XAException(xaCode); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java new file mode 100644 index 0000000000..2c08f1e34a --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.IBMPerfTest; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.ConnectionFactory; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.io.File; +import java.util.Hashtable; + +public class JNDIBindConnectionFactory +{ + + public static final String CONNECTION_FACTORY_BINDING = "amq.ConnectionFactory"; + public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI"; + public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH; + public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory"; + public static final String DEFAULT_CONNECTION_URL = "amqp://guest:guest@clientid/testpath?brokerlist='tcp://localhost:5672'"; + + private static void printUsage() + { + System.out.println("Using default values: Usage:java JNDIBindConnectionFactory [] [] []"); + + } + + public static void main(String[] args) + { + Logger.getRootLogger().setLevel(Level.OFF); + + String connectionFactoryBinding = CONNECTION_FACTORY_BINDING; + String provider = PROVIDER_URL; + String contextFactory = FSCONTEXT_FACTORY; + if (args.length == 0) + { + printUsage(); + System.exit(1); + } + + String connectionURL = args[0]; + + System.out.println("Using Connection:" + connectionURL + "\n"); + + + if (args.length > 1) + { + connectionFactoryBinding = args[1]; + + if (args.length > 2) + { + provider = args[2]; + + if (args.length > 3) + { + contextFactory = args[3]; + } + } + else + { + System.out.println("Using default File System Context Factory"); + System.out.println("Using default Connection Factory Binding:" + connectionFactoryBinding); + } + } + else + { + printUsage(); + } + + + System.out.println("File System Context Factory\n" + + "Connection:" + connectionURL + "\n" + + "Connection Factory Binding:" + connectionFactoryBinding + "\n" + + "JNDI Provider URL:" + provider); + + if (provider.startsWith("file")) + { + File file = new File(provider.substring(provider.indexOf("://") + 3)); + + if (file.exists() && !file.isDirectory()) + { + System.out.println("Couldn't make directory file already exists"); + System.exit(1); + } + else + { + if (!file.exists()) + { + if (!file.mkdirs()) + { + System.out.println("Couldn't make directory"); + System.exit(1); + } + } + } + } + + new JNDIBindConnectionFactory(provider, connectionFactoryBinding, contextFactory, connectionURL); + + } + + public JNDIBindConnectionFactory(String provider, String binding, String contextFactory, String CONNECTION_URL) + { + // Set up the environment for creating the initial context + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); + + env.put(Context.PROVIDER_URL, provider); + + try + { + // Create the initial context + Context ctx = new InitialContext(env); + + // Create the object to be bound + ConnectionFactory factory = null; + + try + { + factory = new AMQConnectionFactory(CONNECTION_URL); + + + try + { + Object obj = ctx.lookup(binding); + + if (obj != null) + { + System.out.println("Un-binding previous Connection Factory"); + ctx.unbind(binding); + } + } + catch (NamingException e) + { + System.out.println("Operation failed: " + e); + } + + // Perform the bind + ctx.bind(binding, factory); + System.out.println("Bound Connection Factory:" + binding); + + // Check that it is bound + Object obj = ctx.lookup(binding); + System.out.println("Connection URL:" + ((AMQConnectionFactory) obj).getConnectionURL()); + + System.out.println("JNDI FS Context:" + provider); + } + catch (NamingException amqe) + { + System.out.println("Operation failed: " + amqe); + } + catch (URLSyntaxException e) + { + System.out.println("Operation failed: " + e); + } + + } + catch (NamingException e) + { + System.out.println("Operation failed: " + e); + } + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java new file mode 100644 index 0000000000..10e8b94311 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java @@ -0,0 +1,213 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.IBMPerfTest; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.io.File; +import java.util.Hashtable; + +public class JNDIBindQueue +{ + public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI"; + public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH; + public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory"; + + Connection _connection = null; + Context _ctx = null; + + + public JNDIBindQueue(String queueBinding, String queueName, String provider, String contextFactory) + { + // Set up the environment for creating the initial context + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); + + env.put(Context.PROVIDER_URL, provider); + + try + { + // Create the initial context + _ctx = new InitialContext(env); + + // Create the object to be bound + + try + { + _connection = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'"); + System.out.println("Connected"); + } + catch (Exception amqe) + { + System.out.println("Unable to create AMQConnectionFactory:" + amqe); + } + + if (_connection != null) + { + bindQueue(queueName, queueBinding); + } + + // Check that it is bound + Object obj = _ctx.lookup(queueBinding); + + System.out.println("Bound Queue:" + ((AMQQueue) obj).toURL()); + + System.out.println("JNDI FS Context:" + provider); + + } + catch (NamingException e) + { + System.out.println("Operation failed: " + e); + } + finally + { + try + { + if (_connection != null) + { + _connection.close(); + } + } + catch (JMSException closeE) + { + System.out.println("Connection closing failed: " + closeE); + } + } + + + } + + + private void bindQueue(String queueName, String queueBinding) throws NamingException + { + + try + { + Object obj = _ctx.lookup(queueBinding); + + if (obj != null) + { + System.out.println("Un-binding exisiting object"); + _ctx.unbind(queueBinding); + } + } + catch (NamingException e) + { + + } + + Queue queue = null; + try + { + + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + if (session != null) + { + queue = ((AMQSession) session).createQueue(queueName); + } + } + catch (JMSException jmse) + { + System.out.println("Unable to create Queue:" + jmse); + } + + // Perform the bind + _ctx.bind(queueBinding, queue); + } + + + public static void main(String[] args) + { + Logger.getRootLogger().setLevel(Level.OFF); + + String provider = JNDIBindQueue.PROVIDER_URL; + String contextFactory = JNDIBindQueue.FSCONTEXT_FACTORY; + + if (args.length > 1) + { + String binding = args[0]; + String queueName = args[1]; + + if (args.length > 2) + { + provider = args[2]; + + if (args.length > 3) + { + contextFactory = args[3]; + } + } + else + { + System.out.println("Using default File System Context Factory"); + } + + System.out.println("File System Context Factory\n" + + "Binding Queue:'" + queueName + "' to '" + binding + "'\n" + + "JNDI Provider URL:" + provider); + + if (provider.startsWith("file")) + { + File file = new File(provider.substring(provider.indexOf("://") + 3)); + + if (file.exists() && !file.isDirectory()) + { + System.out.println("Couldn't make directory file already exists"); + System.exit(1); + } + else + { + if (!file.exists()) + { + if (!file.mkdirs()) + { + System.out.println("Couldn't make directory"); + System.exit(1); + } + } + } + } + + + new JNDIBindQueue(binding, queueName, provider, contextFactory); + + } + else + { + System.out.println("Using Defaults: Usage:java JNDIBindQueue [ []]"); + } + + } + + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java new file mode 100644 index 0000000000..ca071c1187 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java @@ -0,0 +1,212 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.IBMPerfTest; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.io.File; +import java.util.Hashtable; + +public class JNDIBindTopic +{ + public static final String DEFAULT_PROVIDER_FILE_PATH = System.getProperty("java.io.tmpdir") + File.separator + "IBMPerfTestsJNDI"; + public static final String PROVIDER_URL = "file://" + DEFAULT_PROVIDER_FILE_PATH; + + public static final String FSCONTEXT_FACTORY = "com.sun.jndi.fscontext.RefFSContextFactory"; + + Connection _connection = null; + Context _ctx = null; + + + public JNDIBindTopic(String topicBinding, String topicName, String provider, String contextFactory) + { + // Set up the environment for creating the initial context + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); + + env.put(Context.PROVIDER_URL, provider); + + try + { + // Create the initial context + _ctx = new InitialContext(env); + + // Create the object to be bound + + try + { + _connection = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'"); + System.out.println("Connected"); + } + catch (Exception amqe) + { + System.out.println("Unable to create AMQConnectionFactory:" + amqe); + } + + if (_connection != null) + { + bindTopic(topicName, topicBinding); + } + + // Check that it is bound + Object obj = _ctx.lookup(topicBinding); + + System.out.println("Bound Queue:" + ((AMQTopic) obj).toURL()); + + System.out.println("JNDI FS Context:" + provider); + + } + catch (NamingException e) + { + System.out.println("Operation failed: " + e); + } + finally + { + try + { + if (_connection != null) + { + _connection.close(); + } + } + catch (JMSException closeE) + { + System.out.println("Operation failed: " + closeE); + } + } + } + + + private void bindTopic(String topicName, String topicBinding) throws NamingException + { + + try + { + Object obj = _ctx.lookup(topicBinding); + + if (obj != null) + { + System.out.println("Un-binding exisiting object"); + _ctx.unbind(topicBinding); + } + } + catch (NamingException e) + { + + } + + Topic topic = null; + try + { + + Session session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + if (session != null) + { + topic = ((AMQSession) session).createTopic(topicName); + } + } + catch (JMSException jmse) + { + System.out.println("Unable to create Topic:" + jmse); + } + + // Perform the bind + _ctx.bind(topicBinding, topic); + } + + + public static void main(String[] args) + { + Logger.getRootLogger().setLevel(Level.OFF); + + String provider = JNDIBindTopic.PROVIDER_URL; + String contextFactory = JNDIBindTopic.FSCONTEXT_FACTORY; + + if (args.length > 1) + { + String binding = args[0]; + String queueName = args[1]; + + if (args.length > 2) + { + provider = args[2]; + + if (args.length > 3) + { + contextFactory = args[3]; + } + } + else + { + System.out.println("Using default File System Context Factory"); + } + + System.out.println("File System Context Factory\n" + + "Binding Topic:'" + queueName + "' to '" + binding + "'\n" + + "JNDI Provider URL:" + provider); + + + if (provider.startsWith("file")) + { + File file = new File(provider.substring(provider.indexOf("://") + 3)); + + if (file.exists() && !file.isDirectory()) + { + System.out.println("Couldn't make directory file already exists"); + System.exit(1); + } + else + { + if (!file.exists()) + { + if (!file.mkdirs()) + { + System.out.println("Couldn't make directory"); + System.exit(1); + } + } + } + } + + new JNDIBindTopic(binding, queueName, provider, contextFactory); + + } + else + { + System.out.println("Usage:java JNDIBindTopic [ []]"); + } + + } + + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt new file mode 100644 index 0000000000..95ee9f9c77 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt @@ -0,0 +1,11 @@ +These JNDI setup tools are mainly for use in conjunction with the IBM JMS Performance Harness available here: +The jar should be placed in the client/test/lib/ directory. + +http://www.alphaworks.ibm.com/tech/perfharness + + +These JNDI classes use the the SUN FileSystem context. +There are two jar files that should be placed in your client/test/lib directory. + +http://javashoplm.sun.com/ECom/docs/Welcome.jsp?StoreId=22&PartDetailId=7110-jndi-1.2.1-oth-JPR&SiteId=JSC&TransactionId=noreg + diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java new file mode 100644 index 0000000000..cf8059a143 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java @@ -0,0 +1,129 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.cluster; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.MessageListener; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.TextMessage; +import java.util.Random; + +public class Client +{ + private final Random random = new Random(); + private final String name; + private final Session session; + private final MessageProducer topicProducer; + private final MessageProducer queueProducer; + + Client(AMQConnection connection, String name) throws JMSException, InterruptedException + { + this.name = name; + session = connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + AMQTopic topic = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(), new AMQShortString("cluster_test_topic")); + AMQQueue queue = new AMQQueue(((AMQSession)session).getDefaultQueueExchangeName(), new AMQShortString("cluster_test_queue")); + + topicProducer = session.createProducer(topic); + queueProducer = session.createProducer(queue); + + //subscribe to a known topic + session.createConsumer(topic).setMessageListener(new TopicHandler()); + //subscribe to a known queue + session.createConsumer(queue).setMessageListener(new QueueHandler()); + + connection.start(); + + while(true) + { + Thread.sleep(random.nextInt(60000)); + sendToQueue(name + ":" + randomString(5)); + } + } + + private synchronized void sendToTopic(String message) throws JMSException + { + topicProducer.send(session.createTextMessage(message)); + } + + private synchronized void sendToQueue(String message) throws JMSException + { + queueProducer.send(session.createTextMessage(message)); + } + + private String randomString(int length){ + char[] c = new char[length]; + for(int i = 0; i < length; i++) + { + c[i] = (char) ('A' + random.nextInt(26)); + } + return new String(c); + } + + private class QueueHandler implements MessageListener + { + public void onMessage(Message message) + { + try + { + sendToTopic(((TextMessage) message).getText()); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } + } + + private class TopicHandler implements MessageListener + { + public void onMessage(Message message) + { + try + { + System.out.println(((TextMessage) message).getText()); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } + } + + public static void main(String[] argv) throws AMQException, JMSException, InterruptedException, URLSyntaxException + { + //assume args describe the set of brokers to try + + String clientName = argv.length > 1 ? argv[1] : "testClient"; + new Client(new AMQConnection(argv.length > 0 ? argv[0] : "vm://:1", "guest", "guest", clientName, "/test"), clientName); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java new file mode 100644 index 0000000000..1db7e200bd --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java @@ -0,0 +1,277 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.codec; + +import org.apache.qpid.framing.*; +import org.apache.mina.common.*; +import org.apache.mina.common.support.BaseIoSession; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; + +import java.net.SocketAddress; + +/** + */ +public class BasicDeliverTest +{ + public static void main(String[] argv) throws Exception + { + BasicDeliverTest test = new BasicDeliverTest(); + + //warm up: + test.encode(512, 100000); + + //real tests: + test.encode(16, 10000, 15); + test.encode(32, 10000, 15); + test.encode(64, 10000, 15); + test.encode(128, 10000, 15); + test.encode(256, 10000, 15); + test.encode(512, 10000, 15); + test.encode(1024, 10000, 15); + test.encode(2048, 10000, 15); + + test.decode(16, 10000, 15); + test.decode(32, 10000, 15); + test.decode(64, 10000, 15); + test.decode(128, 10000, 15); + test.decode(256, 10000, 15); + test.decode(512, 10000, 15); + test.decode(1024, 10000, 15); + test.decode(2048, 10000, 15); + } + + void decode(int size, int count, int iterations) throws Exception + { + long min = Long.MAX_VALUE; + long max = 0; + long total = 0; + for (int i = 0; i < iterations; i++) + { + long time = decode(size, count); + total += time; + if (time < min) + { + min = time; + } + if (time > max) + { + max = time; + } + } + System.out.println("Decoded " + count + " messages of " + size + + " bytes: avg=" + (total / iterations) + ", min=" + min + ", max=" + max); + } + + + long decode(int size, int count) throws Exception + { + AMQDataBlock block = getDataBlock(size); + ByteBuffer data = ByteBuffer.allocate((int) block.getSize()); // XXX: Is cast a problem? + block.writePayload(data); + data.flip(); + AMQDecoder decoder = new AMQDecoder(false); + long start = System.currentTimeMillis(); + for (int i = 0; i < count; i++) + { + decoder.decode(session, data, decoderOutput); + data.rewind(); + } + return System.currentTimeMillis() - start; + } + + void encode(int size, int count, int iterations) throws Exception + { + long min = Long.MAX_VALUE; + long max = 0; + long total = 0; + for (int i = 0; i < iterations; i++) + { + long time = encode(size, count); + total += time; + if (time < min) + { + min = time; + } + if (time > max) + { + max = time; + } + } + System.out.println("Encoded " + count + " messages of " + size + + " bytes: avg=" + (total / iterations) + ", min=" + min + ", max=" + max); + } + + long encode(int size, int count) throws Exception + { + IoSession session = null; + AMQDataBlock block = getDataBlock(size); + AMQEncoder encoder = new AMQEncoder(); + long start = System.currentTimeMillis(); + for (int i = 0; i < count; i++) + { + encoder.encode(session, block, encoderOutput); + } + return System.currentTimeMillis() - start; + } + + private final ProtocolEncoderOutput encoderOutput = new ProtocolEncoderOutput() + { + + public void write(ByteBuffer byteBuffer) + { + } + + public void mergeAll() + { + } + + public WriteFuture flush() + { + return null; + } + }; + + private final ProtocolDecoderOutput decoderOutput = new ProtocolDecoderOutput() + { + public void write(Object object) + { + } + + public void flush() + { + } + }; + + private final IoSession session = new BaseIoSession() + { + + protected void updateTrafficMask() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public IoService getService() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoServiceConfig getServiceConfig() + { + return null; + } + + public IoHandler getHandler() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoSessionConfig getConfig() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoFilterChain getFilterChain() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public TransportType getTransportType() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getRemoteAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getLocalAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getServiceAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteRequests() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteBytes() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + }; + + private static final char[] DATA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + + static CompositeAMQDataBlock getDataBlock(int size) + { + //create a frame representing message delivery + AMQFrame[] frames = new AMQFrame[3]; + frames[0] = wrapBody(createBasicDeliverBody()); + frames[1] = wrapBody(createContentHeaderBody()); + frames[2] = wrapBody(createContentBody(size)); + + return new CompositeAMQDataBlock(frames); + } + + static AMQFrame wrapBody(AMQBody body) + { + AMQFrame frame = new AMQFrame(1, body); + return frame; + } + + static ContentBody createContentBody(int size) + { + ContentBody body = new ContentBody(); + body.payload = ByteBuffer.allocate(size); + for (int i = 0; i < size; i++) + { + body.payload.put((byte) DATA[i % DATA.length]); + } + return body; + } + + static ContentHeaderBody createContentHeaderBody() + { + ContentHeaderBody body = new ContentHeaderBody(); + body.properties = new BasicContentHeaderProperties(); + body.weight = 1; + body.classId = 6; + return body; + } + + static BasicDeliverBody createBasicDeliverBody() + { + BasicDeliverBody body = new BasicDeliverBody((byte) 8, (byte) 0, + BasicDeliverBody.getClazz((byte) 8, (byte) 0), + BasicDeliverBody.getMethod((byte) 8, (byte) 0), + new AMQShortString("myConsumerTag"), 1, + new AMQShortString("myExchange"), false, + new AMQShortString("myRoutingKey")); + return body; + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Client.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Client.java new file mode 100644 index 0000000000..3886021277 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Client.java @@ -0,0 +1,133 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.codec; + +import org.apache.mina.transport.socket.nio.SocketConnector; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.ContentBody; + +import java.net.InetSocketAddress; + +public class Client extends IoHandlerAdapter +{ + //private static final int[] DEFAULT_SIZES = new int[]{1024, 512, 256, 128, 56}; + //private static final int[] DEFAULT_SIZES = new int[]{256, 256, 256, 256, 256, 512, 512, 512, 512, 512}; + private static final int[] DEFAULT_SIZES = new int[]{256, 512, 256, 512, 256, 512, 256, 512, 256, 512}; + //private static final int[] DEFAULT_SIZES = new int[]{1024, 1024, 1024, 1024, 1024}; + + private final IoSession _session; + private final long _start; + private final int _size; + private final int _count; + private int _received; + private boolean _closed; + + Client(String host, int port, int size, int count) throws Exception + { + _count = count; + _size = size; + AMQDataBlock block = BasicDeliverTest.getDataBlock(size); + + InetSocketAddress address = new InetSocketAddress(host, port); + ConnectFuture future = new SocketConnector().connect(address, this); + future.join(); + _session = future.getSession(); + + _start = System.currentTimeMillis(); + for(int i = 0; i < count; i++) + { + _session.write(block); + } + } + + void close() + { + long time = System.currentTimeMillis() - _start; + System.out.println("Received " + _received + " messages of " + _size + + " bytes in " + time + "ms."); + _session.close(); + synchronized(this) + { + _closed = true; + notify(); + } + } + + void waitForClose() throws InterruptedException + { + synchronized(this) + { + while(!_closed) + { + wait(); + } + } + } + + public void sessionCreated(IoSession session) throws Exception + { + session.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(new AMQCodecFactory(false))); + } + + public void messageReceived(IoSession session, Object object) throws Exception + { + if(isContent(object) && ++_received == _count) close(); + } + + public void exceptionCaught(IoSession session, Throwable throwable) throws Exception + { + throwable.printStackTrace(); + close(); + } + + private static boolean isDeliver(Object o) + { + return o instanceof AMQFrame && ((AMQFrame) o).getBodyFrame() instanceof BasicDeliverBody; + } + + private static boolean isContent(Object o) + { + return o instanceof AMQFrame && ((AMQFrame) o).getBodyFrame() instanceof ContentBody; + } + + public static void main(String[] argv) throws Exception + { + String host = argv.length > 0 ? argv[0] : "localhost"; + int port = argv.length > 1 ? Integer.parseInt(argv[1]) : 8888; + int count = argv.length > 2 ? Integer.parseInt(argv[2]) : 10000; + int[] sizes = argv.length > 3 ? new int[]{Integer.parseInt(argv[3])} : DEFAULT_SIZES; + + System.out.println("Connecting to " + host + ":" + port); + + for(int i = 0; i < sizes.length; i++) + { + new Client(host, port, sizes[i], count).waitForClose(); + Thread.sleep(1000); + } + } + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Server.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Server.java new file mode 100644 index 0000000000..fa4295e0b2 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/codec/Server.java @@ -0,0 +1,103 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.codec; + +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.transport.socket.nio.SocketAcceptor; +import org.apache.mina.util.SessionUtil; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.CompositeAMQDataBlock; + +import java.net.InetSocketAddress; + +public class Server extends IoHandlerAdapter +{ + Server(int port) throws Exception + { + new SocketAcceptor().bind(new InetSocketAddress(port), this); + System.out.println("Listening on " + port); + } + + public void sessionCreated(IoSession session) throws Exception + { + SessionUtil.initialize(session); + session.getFilterChain().addLast("protocolFilter", new ProtocolCodecFilter(new AMQCodecFactory(false))); + } + + public void messageReceived(IoSession session, Object object) throws Exception + { + getAccumulator(session).received(session, (AMQFrame) object); + } + + public void sessionOpened(IoSession session) throws Exception + { + System.out.println("sessionOpened()"); + } + + public void sessionClosed(IoSession session) throws Exception + { + System.out.println("sessionClosed()"); + } + + public void exceptionCaught(IoSession session, Throwable t) throws Exception + { + System.out.println("exceptionCaught()"); + t.printStackTrace(); + session.close(); + } + + private Accumulator getAccumulator(IoSession session) + { + Accumulator a = (Accumulator) session.getAttribute(ACCUMULATOR); + if(a == null) + { + a = new Accumulator(); + session.setAttribute(ACCUMULATOR, a); + } + return a; + } + + private static final String ACCUMULATOR = Accumulator.class.getName(); + + private static class Accumulator + { + private final AMQFrame[] frames = new AMQFrame[3]; + private int i; + + void received(IoSession session, AMQFrame frame) + { + frames[i++] = frame; + if(i >= frames.length) + { + i = 0; + session.write(new CompositeAMQDataBlock(frames)); + } + } + } + + public static void main(String[] argv) throws Exception + { + int port = argv.length > 0 ? Integer.parseInt(argv[0]) : 8888; + new Server(port); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..cac0064785 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java @@ -0,0 +1,35 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.config; + +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.config.ConnectionFactoryInitialiser; +import org.apache.qpid.config.ConnectorConfig; + +import javax.jms.ConnectionFactory; + +class AMQConnectionFactoryInitialiser implements ConnectionFactoryInitialiser +{ + public ConnectionFactory getFactory(ConnectorConfig config) + { + return new AMQConnectionFactory(config.getHost(), config.getPort(), "/test_path"); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java new file mode 100644 index 0000000000..04381d66a0 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.config; + +public abstract class AbstractConfig +{ + public boolean setOptions(String[] argv) + { + try + { + for(int i = 0; i < argv.length - 1; i += 2) + { + String key = argv[i]; + String value = argv[i+1]; + setOption(key, value); + } + return true; + } + catch(Exception e) + { + System.out.println(e.getMessage()); + } + return false; + } + + protected int parseInt(String msg, String i) + { + try + { + return Integer.parseInt(i); + } + catch(NumberFormatException e) + { + throw new RuntimeException(msg + ": " + i); + } + } + + protected long parseLong(String msg, String i) + { + try + { + return Long.parseLong(i); + } + catch(NumberFormatException e) + { + throw new RuntimeException(msg + ": " + i); + } + } + + public abstract void setOption(String key, String value); +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..a9984eb09a --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.config; + +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; + +public interface ConnectionFactoryInitialiser +{ + public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException; +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/Connector.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/Connector.java new file mode 100644 index 0000000000..ff2377f087 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/Connector.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.config; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; + +public class Connector +{ + public Connection createConnection(ConnectorConfig config) throws Exception + { + return getConnectionFactory(config).createConnection(); + } + + ConnectionFactory getConnectionFactory(ConnectorConfig config) throws Exception + { + String factory = config.getFactory(); + if(factory == null) factory = AMQConnectionFactoryInitialiser.class.getName(); + System.out.println("Using " + factory); + return ((ConnectionFactoryInitialiser) Class.forName(factory).newInstance()).getFactory(config); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java new file mode 100644 index 0000000000..b120ed3f12 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.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.config; + +public interface ConnectorConfig +{ + public String getHost(); + public int getPort(); + public String getFactory(); +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..44285efd96 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java @@ -0,0 +1,111 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.config; + +import org.apache.qpid.config.ConnectionFactoryInitialiser; +import org.apache.qpid.config.ConnectorConfig; + +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.MBeanException; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.naming.NameNotFoundException; +import java.util.Hashtable; + +public class JBossConnectionFactoryInitialiser implements ConnectionFactoryInitialiser +{ + public ConnectionFactory getFactory(ConnectorConfig config) throws JMSException + { + ConnectionFactory cf = null; + InitialContext ic = null; + Hashtable ht = new Hashtable(); + ht.put(InitialContext.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); + String jbossHost = System.getProperty("jboss.host", "eqd-lxamq01"); + String jbossPort = System.getProperty("jboss.port", "1099"); + ht.put(InitialContext.PROVIDER_URL, "jnp://" + jbossHost + ":" + jbossPort); + ht.put(InitialContext.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); + + try + { + ic = new InitialContext(ht); + if (!doesDestinationExist("topictest.messages", ic)) + { + deployTopic("topictest.messages", ic); + } + if (!doesDestinationExist("topictest.control", ic)) + { + deployTopic("topictest.control", ic); + } + + cf = (ConnectionFactory) ic.lookup("/ConnectionFactory"); + return cf; + } + catch (NamingException e) + { + throw new JMSException("Unable to lookup object: " + e); + } + catch (Exception e) + { + throw new JMSException("Error creating topic: " + e); + } + } + + private boolean doesDestinationExist(String name, InitialContext ic) throws Exception + { + try + { + ic.lookup("/" + name); + } + catch (NameNotFoundException e) + { + return false; + } + return true; + } + + private void deployTopic(String name, InitialContext ic) throws Exception + { + MBeanServerConnection mBeanServer = lookupMBeanServerProxy(ic); + + ObjectName serverObjectName = new ObjectName("jboss.messaging:service=ServerPeer"); + + String jndiName = "/" + name; + try + { + mBeanServer.invoke(serverObjectName, "createTopic", + new Object[]{name, jndiName}, + new String[]{"java.lang.String", "java.lang.String"}); + } + catch (MBeanException e) + { + System.err.println("Error: " + e); + System.err.println("Cause: " + e.getCause()); + } + } + + private MBeanServerConnection lookupMBeanServerProxy(InitialContext ic) throws NamingException + { + return (MBeanServerConnection) ic.lookup("jmx/invoker/RMIAdaptor"); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java new file mode 100644 index 0000000000..cb8adae18c --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java @@ -0,0 +1,112 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.flow; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; + +public class ChannelFlowTest implements MessageListener +{ + private int sent; + private int received; + + ChannelFlowTest(String broker) throws Exception + { + this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "/test")); + } + + ChannelFlowTest(AMQConnection connection) throws Exception + { + this(connection, new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("ChannelFlowTest")), true)); + } + + ChannelFlowTest(AMQConnection connection, AMQDestination destination) throws Exception + { + AMQSession session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE, 50,25); + + //set up a slow consumer + session.createConsumer(destination).setMessageListener(this); + connection.start(); + + //create a publisher + MessageProducer producer = session.createProducer(destination); + Message msg = session.createTextMessage("Message"); + + //publish in bursts that are fast enough to cause channel flow control + for(int i = 0; i < 10; i++) + { + for(int j = 0; j < 100; j++) + { + producer.send(msg); + sent++; + } + waitUntilReceived(sent - 40); + } + + waitUntilReceived(sent); + + session.close(); + connection.close(); + } + + + private synchronized void waitUntilReceived(int count) throws InterruptedException + { + while(received
  • Connects to a queue, whose name is specified as a cmd-line argument
  • + *
  • Creates a temporary queue
  • + *
  • Creates messages containing a property that is the name of the temporary queue
  • + *
  • Fires off a message on the original queue and waits for a response on the temporary queue
  • + *
+ */ +public class TestLargePublisher +{ + private static final Logger _log = Logger.getLogger(TestLargePublisher.class); + + private AMQConnection _connection; + + private AMQSession _session; + + private class CallbackHandler implements MessageListener + { + private int _expectedMessageCount; + + private int _actualMessageCount; + + private long _startTime; + + public CallbackHandler(int expectedMessageCount, long startTime) + { + _expectedMessageCount = expectedMessageCount; + _startTime = startTime; + } + + public void onMessage(Message m) + { + if (_log.isDebugEnabled()) + { + _log.debug("Message received: " + m); + } + _actualMessageCount++; + if (_actualMessageCount%1000 == 0) + { + _log.info("Received message count: " + _actualMessageCount); + } + /*if (!"henson".equals(m.toString())) + { + _log.error("AbstractJMSMessage response not correct: expected 'henson' but got " + m.toString()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("AbstractJMSMessage " + m + " received"); + } + else + { + _log.info("AbstractJMSMessage received"); + } + } */ + + if (_actualMessageCount == _expectedMessageCount) + { + long timeTaken = System.currentTimeMillis() - _startTime; + System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " + + timeTaken + "ms, equivalent to " + + (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second"); + } + } + } + + public TestLargePublisher(String host, int port, String clientID, + final int messageCount) throws AMQException,URLSyntaxException + { + try + { + createConnection(host, port, clientID); + + _session = (AMQSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + AMQTopic destination = new AMQTopic(_session.getDefaultTopicExchangeName(), new AMQShortString("large")); + MessageProducer producer = (MessageProducer) _session.createProducer(destination); + + _connection.start(); + //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths"); + final long startTime = System.currentTimeMillis(); + + for (int i = 0; i < messageCount; i++) + { + BytesMessage msg = _session.createBytesMessage(); + populateMessage(msg); + producer.send(msg); + } + _log.info("Finished sending " + messageCount + " messages"); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } + + private void createConnection(String host, int port, String clientID) throws AMQException , URLSyntaxException + { + _connection = new AMQConnection(host, port, "guest", "guest", + clientID, "/test"); + } + + private void populateMessage(BytesMessage msg) throws JMSException + { + int size = 1024 * 187; // 187k + byte[] data = new byte[size]; + for (int i = 0; i < data.length; i++) + { + data[i] = (byte)(i%25); + } + msg.writeBytes(data); + } + + /** + * + * @param args argument 1 if present specifies the name of the temporary queue to create. Leaving it blank + * means the server will allocate a name. + */ + public static void main(String[] args) throws URLSyntaxException + { + final String host; + final int port; + final int numMessages; + if (args.length == 0) + { + host = "localhost"; + port = 5672; + numMessages = 100; +// System.err.println("Usage: TestLargePublisher "); + } + else + { + host = args[0]; + port = Integer.parseInt(args[1]); + numMessages = Integer.parseInt(args[2]); + } + + try + { + InetAddress address = InetAddress.getLocalHost(); + String clientID = address.getHostName() + System.currentTimeMillis(); + TestLargePublisher client = new TestLargePublisher(host, port, clientID, numMessages); + } + catch (UnknownHostException e) + { + e.printStackTrace(); + } + catch (AMQException e) + { + System.err.println("Error in client: " + e); + e.printStackTrace(); + } + + //System.exit(0); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java new file mode 100644 index 0000000000..b0cde22349 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java @@ -0,0 +1,167 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.fragmentation; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.jms.Session; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.log4j.Logger; + +import javax.jms.*; +import java.net.InetAddress; + +public class TestLargeSubscriber +{ + private static final Logger _logger = Logger.getLogger(TestLargeSubscriber.class); + + private static MessageProducer _destinationProducer; + + private static String _destinationName; + + public static void main(String[] args) + { + _logger.info("Starting..."); + + final String host; + final int port; + final String username; + final String password; + final String virtualPath; + final int numExpectedMessages; + if (args.length == 0) + { + host = "localhost"; + port = 5672; + username = "guest"; + password = "guest"; + virtualPath = "/test"; + numExpectedMessages = 100; + } + else if (args.length == 6) + { + host = args[0]; + port = Integer.parseInt(args[1]); + username = args[2]; + password = args[3]; + virtualPath = args[4]; + numExpectedMessages = Integer.parseInt(args[5]); + } + else + { + System.out.println("Usage: host port username password virtual-path expectedMessageCount"); + System.exit(1); + throw new RuntimeException("cannot be reached"); + } + + try + { + InetAddress address = InetAddress.getLocalHost(); + AMQConnection con = new AMQConnection(host, port, username, password, + address.getHostName(), virtualPath); + final AMQSession session = (AMQSession) con.createSession(false, Session.AUTO_ACKNOWLEDGE); + + final int expectedMessageCount = numExpectedMessages; + + MessageConsumer consumer = session.createConsumer(new AMQTopic(session.getDefaultTopicExchangeName(), + new AMQShortString("large")), + 100, true, false, null); + + consumer.setMessageListener(new MessageListener() + { + private int _messageCount; + + private long _startTime = 0; + + public void onMessage(Message message) + { + validateMessage(message); + if (_messageCount++ == 0) + { + _startTime = System.currentTimeMillis(); + } + if (_logger.isInfoEnabled()) + { + _logger.info("Got message '" + message + "'"); + } + if (_messageCount == expectedMessageCount) + { + long totalTime = System.currentTimeMillis() - _startTime; + _logger.error("Total time to receive " + _messageCount + " messages was " + + totalTime + "ms. Rate is " + (_messageCount/(totalTime/1000.0))); + } + } + + private void validateMessage(Message message) + { + if (!(message instanceof BytesMessage)) + { + _logger.error("Message is not of correct type - should be BytesMessage and is " + + message.getClass()); + } + BytesMessage bm = (BytesMessage) message; + final int expectedSize = 1024 * 187; // 187k + try + { + if (bm.getBodyLength() != expectedSize) + { + _logger.error("Message is not correct length - should be " + expectedSize + " and is " + + bm.getBodyLength()); + } + } + catch (JMSException e) + { + _logger.error("Failed to validate message: " + e, e); + } + try + { + byte[] data = new byte[(int)bm.getBodyLength()]; + bm.readBytes(data); + for (int i = 0; i < data.length; i++) + { + if (data[i] != (byte)(i%25)) + { + _logger.error("byte " + i + " of message is wrong - should be " + i%25 + " but is " + + data[i]); + } + } + _logger.info("***** Validated message successfully"); + } + catch (JMSException e) + { + _logger.error("Failed to validate message: " + e, e); + } + } + }); + con.start(); + } + catch (Throwable t) + { + System.err.println("Fatal error: " + t); + t.printStackTrace(); + } + + System.out.println("Waiting..."); + } +} + diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java new file mode 100644 index 0000000000..cb5caefc1e --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java @@ -0,0 +1,117 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.headers; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.jms.Session; +//import org.apache.qpid.testutil.Config; + +import javax.jms.MessageListener; +import javax.jms.Message; +import javax.jms.Destination; +import javax.jms.MessageProducer; +import javax.jms.JMSException; + +public class Listener //implements MessageListener +{ +/* private final AMQConnection _connection; + private final MessageProducer _controller; + private final AMQSession _session; + private final MessageFactory _factory; + private int count; + private long start; + + Listener(AMQConnection connection, Destination exchange) throws Exception + { + _connection = connection; + _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _factory = new MessageFactory(_session, 0, 19); + + //register for events + _factory.createConsumer(exchange).setMessageListener(this); + _connection.start(); + + _controller = _session.createProducer(exchange); + } + + private void shutdown() + { + try + { + _session.close(); + _connection.stop(); + _connection.close(); + } + catch(Exception e) + { + e.printStackTrace(System.out); + } + } + + private void report() + { + try + { + String msg = getReport(); + _controller.send(_factory.createReportResponseMessage(msg)); + System.out.println("Sent report: " + msg); + } + catch(Exception e) + { + e.printStackTrace(System.out); + } + } + + private String getReport() throws JMSException + { + long time = (System.currentTimeMillis() - start); + return "Received " + count + " in " + time + "ms"; + } + + public void onMessage(Message message) + { + if(count == 0) start = System.currentTimeMillis(); + + if(_factory.isShutdown(message)) + { + shutdown(); + } + else if(_factory.isReport(message)) + { + //send a report: + report(); + } + else if (++count % 100 == 0) + { + System.out.println("Received " + count + " messages."); + } + } + + public static void main(String[] argv) throws Exception + { + Config config = new Config(); + config.setType(Config.HEADERS); + config.setName("test_headers_exchange"); + config.setOptions(argv); + new Listener((AMQConnection) config.getConnection(), config.getDestination()); + }*/ +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java new file mode 100644 index 0000000000..a2d575fdd4 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.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.headers; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.TextMessage; + +/** + */ +class MessageFactory +{ + private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + + private final AMQSession _session; + private final byte[] _payload; + + private String[] _headerNames; + + MessageFactory(AMQSession session) + { + this(session, Integer.getInteger("amqj.test.message_size", 256).intValue(), 5); + } + + MessageFactory(AMQSession session, int payloadSize, int headerCount) + { + if (headerCount < 1) + { + throw new IllegalArgumentException("Header count must be positive"); + } + _session = session; + _payload = new byte[payloadSize]; + for (int i = 0; i < _payload.length; i++) + { + _payload[i] = (byte) DATA[i % DATA.length]; + } + _headerNames = new String[headerCount]; + // note that with the standard encoding the headers get prefixed with an S to indicate their type + for (int i = 0; i < _headerNames.length; i++) + { + if (i < 10) + { + _headerNames[i] = "F000" + i; + } + else if (i >= 10 && i < 100) + { + _headerNames[i] = "F00" + i; + } + else + { + _headerNames[i] = "F0" + i; + } + } + } + + Message createEventMessage() throws JMSException + { + BytesMessage msg = _session.createBytesMessage(); + if (_payload.length != 0) + { + msg.writeBytes(_payload); + } + return setHeaders(msg, _headerNames); + } + + Message createShutdownMessage() throws JMSException + { + return setHeaders(_session.createMessage(), new String[]{"F0000", "SHUTDOWN"}); + } + + Message createReportRequestMessage() throws JMSException + { + return setHeaders(_session.createMessage(), new String[]{"F0000", "REPORT"}); + } + + Message createReportResponseMessage(String msg) throws JMSException + { + return setHeaders(_session.createTextMessage(msg), new String[]{"CONTROL", "REPORT"}); + } + + boolean isShutdown(Message m) + { + return checkPresent(m, "SHUTDOWN"); + } + + boolean isReport(Message m) + { + return checkPresent(m, "REPORT"); + } + + Object getReport(Message m) + { + try + { + return ((TextMessage) m).getText(); + } + catch (JMSException e) + { + e.printStackTrace(System.out); + return e.toString(); + } + } + + FieldTable getConsumerBinding() + { + FieldTable binding = FieldTableFactory.newFieldTable(); + binding.setString("SF0000", "value"); + return binding; + } + + FieldTable getControllerBinding() + { + FieldTable binding = FieldTableFactory.newFieldTable(); + binding.setString("SCONTROL", "value"); + return binding; + } + + MessageConsumer createConsumer(Destination source) throws Exception + { + return _session.createConsumer(source, 0, false, true, null, getConsumerBinding()); + } + + MessageConsumer createController(Destination source) throws Exception + { + return _session.createConsumer(source, 0, false, true, null, getControllerBinding()); + } + + private static boolean checkPresent(Message m, String s) + { + try + { + return m.getStringProperty(s) != null; + } + catch (JMSException e) + { + e.printStackTrace(System.out); + return false; + } + } + + private static Message setHeaders(Message m, String[] headers) throws JMSException + { + for (int i = 0; i < headers.length; i++) + { + // the value in GRM is 5 bytes + m.setStringProperty(headers[i], "value"); + } + return m; + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java new file mode 100644 index 0000000000..d9ef702c48 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java @@ -0,0 +1,133 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.headers; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +//import org.apache.qpid.testutil.Config; + +import javax.jms.*; + +public class Publisher // implements MessageListener +{ +/* private final Object _lock = new Object(); + private final AMQConnection _connection; + private final AMQSession _session; + private final Destination _exchange; + private final MessageFactory _factory; + private final MessageProducer _publisher; + private int _count; + + Publisher(AMQConnection connection, Destination exchange) throws Exception + { + _connection = connection; + _exchange = exchange; + _session = (AMQSession) _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _factory = new MessageFactory(_session, 0, 19); + _publisher = _session.createProducer(_exchange); + } + + Publisher(Config config) throws Exception + { + this((AMQConnection) config.getConnection(), config.getDestination()); + } + + private void test(int msgCount, int consumerCount) throws Exception + { + _count = consumerCount; + _factory.createController(_exchange).setMessageListener(this); + _connection.start(); + long start = System.currentTimeMillis(); + publish(msgCount); + waitForCompletion(consumerCount); + long end = System.currentTimeMillis(); + + System.out.println("Completed in " + (end - start) + " ms."); + + //request shutdown + _publisher.send(_factory.createShutdownMessage()); + + _connection.stop(); + _connection.close(); + } + + private void publish(int count) throws Exception + { + + //send events + for (int i = 0; i < count; i++) + { + _publisher.send(_factory.createEventMessage()); + if ((i + 1) % 100 == 0) + { + System.out.println("Sent " + (i + 1) + " messages"); + } + } + + //request report + _publisher.send(_factory.createReportRequestMessage()); + } + + private void waitForCompletion(int consumers) throws Exception + { + System.out.println("Waiting for completion..."); + synchronized (_lock) + { + while (_count > 0) + { + _lock.wait(); + } + } + } + + + public void onMessage(Message message) + { + System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining"); + if (_count == 0) + { + synchronized (_lock) + { + _lock.notify(); + } + } + } + + + public static void main(String[] argv) throws Exception + { + if (argv.length >= 2) + { + int msgCount = Integer.parseInt(argv[argv.length - 2]); + int consumerCount = Integer.parseInt(argv[argv.length - 1]); + + Config config = new Config(); + config.setType(Config.HEADERS); + config.setName("test_headers_exchange"); + String[] options = new String[argv.length - 2]; + System.arraycopy(argv, 0, options, 0, options.length); + config.setOptions(options); + + new Publisher(config).test(msgCount, consumerCount); + } + + }*/ +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java new file mode 100644 index 0000000000..ee6a12c233 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java @@ -0,0 +1,273 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jndi.referenceable; + +import org.apache.qpid.client.*; +import org.apache.qpid.AMQException; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.*; +import javax.naming.*; + +import java.util.Properties; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; + +/** + * Binds a reference from a JNDI source. + * Given a properties file with the JNDI information and a binding string. + */ +public class Bind +{ + private static final String USAGE="USAGE: java bind -cf | -c [-t ] [-q ]"; + public Bind(String propertiesFile, String bindingURL, Referenceable reference) throws NameAlreadyBoundException, NoInitialContextException + { + // Set up the environment for creating the initial context + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + { + qpid_home += "/"; + } + + try + { + InputStream inputStream = new FileInputStream(qpid_home + propertiesFile); + Properties properties = new Properties(); + properties.load(inputStream); + + // Create the initial context + Context ctx = new InitialContext(properties); + + // Perform the binds + ctx.bind(bindingURL, reference); + + // Close the context when we're done + ctx.close(); + } + catch (IOException ioe) + { + System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe); + } + catch (NamingException e) + { + System.out.println("Operation failed: " + e); + if (e instanceof NameAlreadyBoundException) + { + throw (NameAlreadyBoundException) e; + } + + if (e instanceof NoInitialContextException) + { + throw (NoInitialContextException) e; + } + } + + } + + private static String parse(String[] args, int index, String what, String type) + { + try + { + return args[index]; + } + catch (IndexOutOfBoundsException ioobe) + { + System.out.println("ERROR: No " + what + " specified for " + type + "."); + System.out.println(USAGE); + System.exit(1); + } + + // The path is either return normally or exception.. which calls system exit so keep the compiler happy + return "Never going to happen"; + } + + + public static void main(String[] args) throws NameAlreadyBoundException, NoInitialContextException, URLSyntaxException, AMQException, JMSException + { + + + org.apache.log4j.Logger.getRootLogger().setLevel(org.apache.log4j.Level.OFF); + +// org.apache.log4j.Logger _logger = org.apache.log4j.Logger.getLogger(AMQConnection.class); +// _logger.setLevel(org.apache.log4j.Level.OFF); + + boolean exit = false; + + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + exit = true; + } + + if (args.length <= 2) + { + System.out.println("At least a connection or connection factory must be requested to be bound."); + exit = true; + } + else + { + if ((args.length - 1) % 3 != 0) + { + System.out.println("Not all values have full details"); + exit = true; + } + } + if (exit) + { + System.out.println(USAGE); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + + { + qpid_home += "/"; + } + + AMQConnectionFactory cf = null; + AMQConnection c = null; + AMQSession session = null; + Referenceable reference = null; + + for (int index = 1; index < args.length; index ++) + { + String obj = args[index]; + + String what = "Invalid"; + String binding; + + if (obj.startsWith("-c")) + { + boolean isFactory = obj.contains("f"); + + + if (isFactory) + { + what = "ConnectionFactory"; + } + else + { + what = "Factory"; + } + + String url = parse(args, ++index, "url", what); + + if (isFactory) + { + + cf = new AMQConnectionFactory(url); + reference = cf; + } + else + { + c = new AMQConnection(url); + reference = c; + } + + } + + if (obj.equals("-t") || obj.equals("-q")) + { + if (c == null) + { + c = (AMQConnection) cf.createConnection(); + } + + if (session == null) + { + session = (AMQSession) c.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + } + + if (obj.equals("-t")) + { + + String topicName = parse(args, ++index, "Topic Name", "Topic"); + reference = (AMQTopic) session.createTopic(topicName); + what = "Topic"; + } + else + { + if (obj.equals("-q")) + { + String topicName = parse(args, ++index, "Queue Name", "Queue"); + reference = (AMQQueue) session.createQueue(topicName); + what = "Queue"; + } + } + + binding = parse(args, ++index, "binding", what); + if (binding == null) + { + System.out.println(obj + " is not a known Object to bind."); + System.exit(1); + } + else + { + System.out.print("Binding:" + reference + " to " + binding); + try + { + new Bind(args[0], binding, reference); + System.out.println(" ..Successful"); + + } + catch (NameAlreadyBoundException nabe) + { + System.out.println(""); + if (!obj.startsWith("-c") || index == args.length - 1) + { + throw nabe; + } + else + { + System.out.println("Continuing with other bindings using the same connection details"); + } + } + finally + { + if (!obj.startsWith("-c") || index == args.length - 1) + { + if (c != null) + { + c.close(); + } + } + } + } + } + + if (c != null) + { + c.close(); + } + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java new file mode 100644 index 0000000000..1c9d8b0fd5 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java @@ -0,0 +1,196 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jndi.referenceable; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Looksup a reference from a JNDI source. + * Given a properties file with the JNDI information and a binding string. + */ +public class Lookup +{ + private static final String USAGE = "USAGE: java lookup -b "; + private Properties _properties; + private Object _object; + + public Lookup(String propertiesFile, String bindingValue) throws NamingException + { + // Set up the environment for creating the initial context + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + { + qpid_home += "/"; + } + + try + { + InputStream inputStream = new FileInputStream(qpid_home + propertiesFile); + Properties properties = new Properties(); + properties.load(inputStream); + + _properties = properties; + lookup(bindingValue); + } + catch (IOException ioe) + { + System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe); + } + } + + public Object lookup(String bindingValue) throws NamingException + { + + // Create the initial context + Context ctx = new InitialContext(_properties); + + // Perform the binds + _object = ctx.lookup(bindingValue); + + // Close the context when we're done + ctx.close(); + + return getObject(); + } + + public Object getObject() + { + return _object; + } + + private static String parse(String[] args, int index, String what) + { + try + { + return args[index]; + } + catch (IndexOutOfBoundsException ioobe) + { + System.out.println("ERROR: No " + what + " specified."); + System.out.println(USAGE); + System.exit(1); + } + + // The path is either return normally or exception.. which calls system exit so keep the compiler happy + return "Never going to happen"; + } + + + public static void main(String[] args) throws NamingException + { + boolean exit = false; + + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + exit = true; + } + + if (args.length <= 2) + { + System.out.println("At least a connection or connection factory must be requested to be bound."); + exit = true; + } + else + { + if ((args.length - 1) % 2 != 0) + { + System.out.println("Not all values have full details"); + exit = true; + } + } + if (exit) + { + System.out.println(USAGE); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + + { + qpid_home += "/"; + } + + for (int index = 1; index < args.length; index ++) + { + String obj = args[index]; + + + if (obj.equals("-b")) + { + String binding = parse(args, ++index, "binding"); + + if (binding == null) + { + System.out.println("Binding not specified."); + System.exit(1); + } + else + { + System.out.print("Looking up:" + binding); + try + { + Lookup l = new Lookup(args[0], binding); + + Object object = l.getObject(); + + if (object instanceof Connection) + { + try + { + ((Connection) object).close(); + } + catch (JMSException jmse) + { + ; + } + } + } + catch (NamingException nabe) + { + System.out.println("Problem unbinding " + binding + " continuing with other values."); + } + } + }// if -b + else + { + System.out.println("Continuing with other bindings option not known:" + obj); + } + }//for + }//main +}//class diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java new file mode 100644 index 0000000000..1acead674c --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java @@ -0,0 +1,166 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.jndi.referenceable; + +import javax.naming.*; + +import java.util.Properties; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.IOException; + +/** + * Unbinds a reference from a JNDI source. + * Given a properties file with the JNDI information and a binding string. + */ +public class Unbind +{ + private static final String USAGE = "USAGE: java unbind -b "; + + public Unbind(String propertiesFile, String bindingValue) throws NamingException + { + // Set up the environment for creating the initial context + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + { + qpid_home += "/"; + } + + try + { + InputStream inputStream = new FileInputStream(qpid_home + propertiesFile); + Properties properties = new Properties(); + properties.load(inputStream); + + // Create the initial context + Context ctx = new InitialContext(properties); + + // Perform the binds + ctx.unbind(bindingValue); + + // Close the context when we're done + ctx.close(); + } + catch (IOException ioe) + { + System.out.println("Unable to access properties file:" + propertiesFile + " Due to:" + ioe); + } + } + + private static String parse(String[] args, int index, String what) + { + try + { + return args[index]; + } + catch (IndexOutOfBoundsException ioobe) + { + System.out.println("ERROR: No " + what + " specified."); + System.out.println(USAGE); + System.exit(1); + } + + // The path is either return normally or exception.. which calls system exit so keep the compiler happy + return "Never going to happen"; + } + + + public static void main(String[] args) throws NamingException + { + boolean exit = false; + + String qpid_home = System.getProperty("QPID_HOME"); + + if (qpid_home == null || qpid_home.equals("")) + { + System.out.println("QPID_HOME is not set"); + exit = true; + } + + if (args.length <= 2) + { + System.out.println("At least a connection or connection factory must be requested to be bound."); + exit = true; + } + else + { + if ((args.length - 1) % 2 != 0) + { + System.out.println("Not all values have full details"); + exit = true; + } + } + if (exit) + { + System.out.println(USAGE); + System.exit(1); + } + + if (qpid_home.charAt(qpid_home.length() - 1) != '/') + + { + qpid_home += "/"; + } + + for (int index = 1; index < args.length; index ++) + { + String obj = args[index]; + + + if (obj.equals("-b")) + { + String binding = parse(args, ++index, "binding"); + + if (binding == null) + { + System.out.println("Binding not specified."); + System.exit(1); + } + else + { + System.out.print("UnBinding:" + binding); + try + { + new Unbind(args[0], binding); + System.out.println(" ..Successful"); + } + catch (NamingException nabe) + { + System.out.println(""); + + System.out.println("Problem unbinding " + binding + " continuing with other values."); + } + } + }// if -b + else + { + System.out.println("Continuing with other bindings option not known:" + obj); + } + }//for + }//main +}//class diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java new file mode 100644 index 0000000000..4865a68dc4 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java @@ -0,0 +1,153 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.latency; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.MessageProducer; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.JMSException; +import javax.jms.TextMessage; +import javax.jms.BytesMessage; + +public class LatencyTest implements MessageListener +{ + private volatile boolean waiting; + private int sent; + private int received; + + private final byte[] data; + + private long min = Long.MAX_VALUE; + private long max = 0; + private long total = 0; + + LatencyTest(String broker, int count, int delay, int length) throws Exception + { + this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "/test"), count, delay, length); + } + + LatencyTest(AMQConnection connection, int count, int delay, int length) throws Exception + { + this(connection, new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("LatencyTest")), true), count, delay, length); + } + + LatencyTest(AMQConnection connection, AMQDestination destination, int count, int delay, int length) throws Exception + { + AMQSession session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + data = new byte[length]; + for(int i = 0; i < data.length; i++) + { + data[i] = (byte) (i % 100); + } + + //set up a consumer + session.createConsumer(destination).setMessageListener(this); + connection.start(); + + //create a publisher + MessageProducer producer = session.createProducer(destination, false, false, true); + + //publish at a low volume + for(int i = 0; i < count; i++) + { + BytesMessage msg = session.createBytesMessage(); + msg.writeBytes(data); + msg.setStringProperty("sent-at", Long.toString(System.nanoTime())); + producer.send(msg); + Thread.sleep(delay); + if(++sent % 100 == 0) + { + System.out.println("Sent " + sent + " of " + count); + } + } + + waitUntilReceived(sent); + + session.close(); + connection.close(); + + System.out.println("Latency (in nanoseconds): avg=" + (total/sent) + ", min=" + min + ", max=" + max + + ", avg(discarding min and max)=" + ((total - min - max) / (sent - 2))); + } + + + private synchronized void waitUntilReceived(int count) throws InterruptedException + { + waiting = true; + while(received < count) + { + wait(); + } + waiting = false; + } + + public void onMessage(Message message) + { + received++; + try + { + long sent = Long.parseLong(message.getStringProperty("sent-at")); + long time = System.nanoTime() - sent; + total += time; + min = Math.min(min, time); + max = Math.max(max, time); + } + catch (JMSException e) + { + e.printStackTrace(); + } + + if(waiting){ + synchronized(this) + { + notify(); + } + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + String host = argv.length > 0 ? argv[0] : "localhost:5672"; + if("-help".equals(host)) + { + System.out.println("Usage: "); + } + int count = argv.length > 1 ? Integer.parseInt(argv[1]) : 1000; + int delay = argv.length > 2 ? Integer.parseInt(argv[2]) : 1000; + int size = argv.length > 3 ? Integer.parseInt(argv[3]) : 512; + new LatencyTest(host, count, delay, size); + } + + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java new file mode 100644 index 0000000000..f0ac0e6902 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.mina; + +import org.apache.log4j.Logger; +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.transport.socket.nio.SocketAcceptor; +import org.apache.mina.transport.socket.nio.SocketAcceptorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; +import org.apache.qpid.pool.ReadWriteThreadModel; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import junit.framework.TestCase; + +/** + * Tests MINA socket performance. This acceptor simply reads data from the network and writes it back again. + * + */ +public class AcceptorTest extends TestCase +{ + private static final Logger _logger = Logger.getLogger(AcceptorTest.class); + + public static int PORT = 9999; + + private static class TestHandler extends IoHandlerAdapter + { + private int _sentCount; + + private int _bytesSent; + + public void messageReceived(IoSession session, Object message) throws Exception + { + ((ByteBuffer) message).acquire(); + session.write(message); + _logger.debug("Sent response " + ++_sentCount); + _bytesSent += ((ByteBuffer)message).remaining(); + _logger.debug("Bytes sent: " + _bytesSent); + } + + public void messageSent(IoSession session, Object message) throws Exception + { + //((ByteBuffer) message).release(); + } + + public void exceptionCaught(IoSession session, Throwable cause) throws Exception + { + _logger.error("Error: " + cause, cause); + } + } + + public void testStartAcceptor() throws IOException + { + IoAcceptor acceptor = null; + acceptor = new SocketAcceptor(); + + SocketAcceptorConfig config = (SocketAcceptorConfig) acceptor.getDefaultConfig(); + SocketSessionConfig sc = (SocketSessionConfig) config.getSessionConfig(); + sc.setTcpNoDelay(true); + sc.setSendBufferSize(32768); + sc.setReceiveBufferSize(32768); + + config.setThreadModel(ReadWriteThreadModel.getInstance()); + + acceptor.bind(new InetSocketAddress(PORT), + new TestHandler()); + _logger.info("Bound on port " + PORT); + } + + public static void main(String[] args) throws IOException + { + AcceptorTest a = new AcceptorTest(); + a.testStartAcceptor(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(AcceptorTest.class); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java new file mode 100644 index 0000000000..bfe29c47e6 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.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.mina; + +import org.apache.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; + +import junit.framework.TestCase; + +public class BlockingAcceptorTest extends TestCase +{ + private static final Logger _logger = Logger.getLogger(BlockingAcceptorTest.class); + + public static int PORT = 9999; + + public void testStartAcceptor() throws IOException + { + + ServerSocket sock = new ServerSocket(PORT); + + sock.setReuseAddress(true); + sock.setReceiveBufferSize(32768); + _logger.info("Bound on port " + PORT); + + while (true) + { + final Socket s = sock.accept(); + _logger.info("Received connection from " + s.getRemoteSocketAddress()); + s.setReceiveBufferSize(32768); + s.setSendBufferSize(32768); + s.setTcpNoDelay(true); + new Thread(new Runnable() + { + public void run() + { + byte[] chunk = new byte[32768]; + try + { + InputStream is = s.getInputStream(); + OutputStream os = s.getOutputStream(); + + while (true) + { + int count = is.read(chunk, 0, chunk.length); + if (count > 0) + { + os.write(chunk, 0, count); + } + } + } + catch (IOException e) + { + _logger.error("Error - closing connection: " + e, e); + } + } + }, "SocketReaderWriter").start(); + } + } + + public static void main(String[] args) throws IOException + { + BlockingAcceptorTest a = new BlockingAcceptorTest(); + a.testStartAcceptor(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(AcceptorTest.class); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java new file mode 100644 index 0000000000..910345624f --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java @@ -0,0 +1,271 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.mina; + +import org.apache.log4j.Logger; +import org.apache.mina.common.*; +import org.apache.mina.transport.socket.nio.SocketConnector; +import org.apache.mina.transport.socket.nio.SocketConnectorConfig; +import org.apache.mina.transport.socket.nio.SocketSessionConfig; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.concurrent.CountDownLatch; + +import junit.framework.TestCase; + +public class WriterTest extends TestCase +{ + private static final Logger _logger = Logger.getLogger(WriterTest.class); + + private static class RunnableWriterTest implements Runnable + { + private Logger _logger; + + private IoSession _session; + + private long _startTime; + + private long[] _chunkTimes; + + private int _chunkCount = 500000; + + private int _chunkSize = 1024; + + private CountDownLatch _notifier; + + public RunnableWriterTest(Logger logger) + { + _logger = logger; + } + + public void run() + { + _startTime = System.currentTimeMillis(); + _notifier = new CountDownLatch(1); + for (int i = 0; i < _chunkCount; i++) + { + ByteBuffer buf = ByteBuffer.allocate(_chunkSize, false); + byte check = (byte) (i % 128); + buf.put(check); + buf.fill((byte)88, buf.remaining()); + buf.flip(); + _session.write(buf); + } + + try + { + _logger.info("All buffers sent; waiting for receipt from server"); + _notifier.await(); + } + catch (InterruptedException e) + { + } + _logger.info("Completed"); + long totalTime = System.currentTimeMillis() - _startTime; + _logger.info("Total time: " + totalTime); + _logger.info("MB per second: " + (_chunkSize * _chunkCount)/totalTime); + long lastChunkTime = _startTime; + double average = 0; + for (int i = 0; i < _chunkTimes.length; i++) + { + if (i == 0) + { + average = _chunkTimes[i] - _startTime; + } + else + { + long delta = _chunkTimes[i] - lastChunkTime; + if (delta != 0) + { + average = (average + delta)/2; + } + } + lastChunkTime = _chunkTimes[i]; + } + _logger.info("Average chunk time: " + average + "ms"); + CloseFuture cf = _session.close(); + cf.join(); + } + + private class WriterHandler extends IoHandlerAdapter + { + private int _chunksReceived = 0; + + private int _partialBytesRead = 0; + + private byte _partialCheckNumber; + + private int _totalBytesReceived = 0; + + public void messageReceived(IoSession session, Object message) throws Exception + { + ByteBuffer result = (ByteBuffer) message; + _totalBytesReceived += result.remaining(); + int size = result.remaining(); + long now = System.currentTimeMillis(); + if (_partialBytesRead > 0) + { + int offset = _chunkSize - _partialBytesRead; + if (size >= offset) + { + _chunkTimes[_chunksReceived++] = now; + result.position(offset); + } + else + { + // have not read even one chunk, including the previous partial bytes + _partialBytesRead += size; + return; + } + } + + int chunkCount = result.remaining()/_chunkSize; + + for (int i = 0; i < chunkCount; i++) + { + _chunkTimes[_chunksReceived++] = now; + byte check = result.get(); + _logger.debug("Check number " + check + " read"); + if (check != (byte)((_chunksReceived - 1)%128)) + { + _logger.error("Check number " + check + " read when expected " + (_chunksReceived%128)); + } + _logger.debug("Chunk times recorded"); + + try + { + result.skip(_chunkSize - 1); + } + catch (IllegalArgumentException e) + { + _logger.error("Position was: " + result.position()); + _logger.error("Tried to skip to: " + (_chunkSize * i)); + _logger.error("limit was; " + result.limit()); + } + } + _logger.debug("Chunks received now " + _chunksReceived); + _logger.debug("Bytes received: " + _totalBytesReceived); + _partialBytesRead = result.remaining(); + + if (_partialBytesRead > 0) + { + _partialCheckNumber = result.get(); + } + + if (_chunksReceived >= _chunkCount) + { + _notifier.countDown(); + } + + } + + public void exceptionCaught(IoSession session, Throwable cause) throws Exception + { + _logger.error("Error: " + cause, cause); + } + } + + public void startWriter(int chunkSize) throws IOException, InterruptedException + { + _chunkSize = chunkSize; + + IoConnector ioConnector = null; + + ioConnector = new SocketConnector(); + + SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig(); + cfg.setThreadModel(ThreadModel.MANUAL); + SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); + scfg.setTcpNoDelay(true); + scfg.setSendBufferSize(32768); + scfg.setReceiveBufferSize(32768); + + final InetSocketAddress address = new InetSocketAddress("localhost", AcceptorTest.PORT); + _logger.info("Attempting connection to " + address); + ConnectFuture future = ioConnector.connect(address, new WriterHandler()); + // wait for connection to complete + future.join(); + _logger.info("Connection completed"); + // we call getSession which throws an IOException if there has been an error connecting + _session = future.getSession(); + _chunkTimes = new long[_chunkCount]; + Thread t = new Thread(this); + t.start(); + t.join(); + _logger.info("Test completed"); + } + } + + private RunnableWriterTest _runnableWriterTest = new RunnableWriterTest(_logger); + + public void test1k() throws IOException, InterruptedException + { + _logger.info("Starting 1k test"); + _runnableWriterTest.startWriter(1024); + } + + public void test2k() throws IOException, InterruptedException + { + _logger.info("Starting 2k test"); + _runnableWriterTest.startWriter(2048); + } + + public void test4k() throws IOException, InterruptedException + { + _logger.info("Starting 4k test"); + _runnableWriterTest.startWriter(4096); + } + + public void test8k() throws IOException, InterruptedException + { + _logger.info("Starting 8k test"); + _runnableWriterTest.startWriter(8192); + } + + public void test16k() throws IOException, InterruptedException + { + _logger.info("Starting 16k test"); + _runnableWriterTest.startWriter(16384); + } + + public void test32k() throws IOException, InterruptedException + { + _logger.info("Starting 32k test"); + _runnableWriterTest.startWriter(32768); + } + + public static void main(String[] args) throws IOException, InterruptedException + { + WriterTest w = new WriterTest(); + //w.test1k(); + //w.test2k(); + //w.test4k(); + w.test8k(); + //w.test16k(); + //w.test32k(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(WriterTest.class); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java new file mode 100644 index 0000000000..db02b9954a --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java @@ -0,0 +1,269 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.multiconsumer; + +import java.io.ByteArrayOutputStream; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import junit.framework.TestCase; + +import org.apache.commons.codec.binary.Base64; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.jms.Session; + +/** + * Test AMQ. + */ +public class AMQTest extends TestCase implements ExceptionListener +{ + + private final static String COMPRESSION_PROPNAME = "_MSGAPI_COMP"; + private final static String UTF8 = "UTF-8"; + private static final String SUBJECT = "test.amq"; + private static final String DUMMYCONTENT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String HUGECONTENT; + + private AMQConnection connect = null; + private Session pubSession = null; + private Session subSession = null; + private Topic topic = null; + + static + { + StringBuilder sb = new StringBuilder(DUMMYCONTENT.length() * 115); + for (int i = 0; i < 100; i++) + { + sb.append(DUMMYCONTENT); + } + HUGECONTENT = sb.toString(); + } + + private void setup() throws Exception + { + connect = new AMQConnection("localhost", 5672, "guest", "guest", "client1", "/"); + connect.setExceptionListener(this); + pubSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + subSession = connect.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + topic = new AMQTopic(pubSession.getDefaultTopicExchangeName(), new AMQShortString(SUBJECT)); + + connect.start(); + } + + public void testMultipleListeners() throws Exception + { + setup(); + try + { + // Create 5 listeners + MsgHandler[] listeners = new MsgHandler[5]; + for (int i = 0; i < listeners.length; i++) + { + listeners[i] = new MsgHandler(); + MessageConsumer subscriber = subSession.createConsumer(topic); + subscriber.setMessageListener(listeners[i]); + } + MessageProducer publisher = pubSession.createProducer(topic); + // Send a single message + TextMessage msg = pubSession.createTextMessage(); + msg.setText(DUMMYCONTENT); + publisher.send(msg); + Thread.sleep(5000); + // Check listeners to ensure they all got it + for (int i = 0; i < listeners.length; i++) + { + if (listeners[i].isGotIt()) + { + System.out.println("Got callback for listener " + i); + } + else + { + TestCase.fail("Listener " + i + " did not get callback"); + } + } + } + catch (Throwable e) + { + System.err.println("Error: " + e); + e.printStackTrace(System.err); + } + finally + { + close(); + } + } + + public void testCompression() throws Exception + { + setup(); + String comp = this.compressString(HUGECONTENT); + try + { + MsgHandler listener = new MsgHandler(); + MessageConsumer subscriber = subSession.createConsumer(topic); + subscriber.setMessageListener(listener); + MessageProducer publisher = pubSession.createProducer(topic); + + // Send a single message + TextMessage msg = pubSession.createTextMessage(); + // Set the compressed text + msg.setText(comp); + msg.setBooleanProperty(COMPRESSION_PROPNAME, true); + publisher.send(msg); + Thread.sleep(1000); + // Check listeners to ensure we got it + if (listener.isGotIt()) + { + System.out.println("Got callback for listener"); + } + else + { + TestCase.fail("Listener did not get callback"); + } + } + finally + { + close(); + } + } + + private void close() throws Exception + { + if (connect != null) + { + connect.close(); + } + } + + private class MsgHandler implements MessageListener + { + private boolean gotIt = false; + + public void onMessage(Message msg) + { + try + { + TextMessage textMessage = (TextMessage) msg; + String string = textMessage.getText(); + if (string != null && string.length() > 0) + { + gotIt = true; + } + if (textMessage.getBooleanProperty(COMPRESSION_PROPNAME)) + { + string = inflateString(string); + } + System.out.println("Got callback of size " + (string==null?0:string.length())); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public boolean isGotIt() + { + return this.gotIt; + } + } + + private String compressString(String string) throws Exception + { + long start = System.currentTimeMillis(); + byte[] input = string.getBytes(); + Deflater compressor = new Deflater(Deflater.BEST_COMPRESSION); + compressor.setInput(input); + compressor.finish(); + + // Get byte array from output of compressor + ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length); + byte[] buf = new byte[1024]; + while (!compressor.finished()) + { + int cnt = compressor.deflate(buf); + baos.write(buf, 0, cnt); + } + baos.close(); + byte[] output = baos.toByteArray(); + + // Convert byte array into String + byte[] base64 = Base64.encodeBase64(output); + String sComp = new String(base64, UTF8); + + long diff = System.currentTimeMillis() - start; + System.out.println("Compressed text from " + input.length + " to " + + sComp.getBytes().length + " in " + diff + " ms"); + System.out.println("Compressed text = '" + sComp + "'"); + + return sComp; + } + + private String inflateString(String string) throws Exception + { + byte[] input = string.getBytes(); + + // First convert Base64 string back to binary array + byte[] bytes = Base64.decodeBase64(input); + + // Set string as input data for decompressor + Inflater decompressor = new Inflater(); + decompressor.setInput(bytes); + + // Decompress the data + ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length); + byte[] buf = new byte[1024]; + while (!decompressor.finished()) + { + int count = decompressor.inflate(buf); + bos.write(buf, 0, count); + } + bos.close(); + byte[] output = bos.toByteArray(); + + // Get the decompressed data + return new String(output, UTF8); + } + + /** + * @see javax.jms.ExceptionListener#onException(javax.jms.JMSException) + */ + public void onException(JMSException e) + { + System.err.println(e.getMessage()); + e.printStackTrace(System.err); + } + + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java new file mode 100644 index 0000000000..37b4ff1498 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.pubsub1; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.jms.MessageProducer; +import org.apache.qpid.jms.Session; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * A client that behaves as follows: + *
  • Connects to a queue, whose name is specified as a cmd-line argument
  • + *
  • Creates a temporary queue
  • + *
  • Creates messages containing a property that is the name of the temporary queue
  • + *
  • Fires off a message on the original queue and waits for a response on the temporary queue
  • + *
+ * + */ +public class TestPublisher +{ + private static final Logger _log = Logger.getLogger(TestPublisher.class); + + private AMQConnection _connection; + + private Session _session; + + private class CallbackHandler implements MessageListener + { + private int _expectedMessageCount; + + private int _actualMessageCount; + + private long _startTime; + + public CallbackHandler(int expectedMessageCount, long startTime) + { + _expectedMessageCount = expectedMessageCount; + _startTime = startTime; + } + + public void onMessage(Message m) + { + if (_log.isDebugEnabled()) + { + _log.debug("Message received: " + m); + } + _actualMessageCount++; + if (_actualMessageCount%1000 == 0) + { + _log.info("Received message count: " + _actualMessageCount); + } + /*if (!"henson".equals(m.toString())) + { + _log.error("AbstractJMSMessage response not correct: expected 'henson' but got " + m.toString()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("AbstractJMSMessage " + m + " received"); + } + else + { + _log.info("AbstractJMSMessage received"); + } + } */ + + if (_actualMessageCount == _expectedMessageCount) + { + long timeTaken = System.currentTimeMillis() - _startTime; + System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " + + timeTaken + "ms, equivalent to " + + (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second"); + } + } + } + + public TestPublisher(String host, int port, String clientID, String commandQueueName, + final int messageCount) throws AMQException, URLSyntaxException + { + try + { + createConnection(host, port, clientID); + + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + AMQTopic destination = new AMQTopic(_session.getDefaultTopicExchangeName(), new AMQShortString(commandQueueName)); + MessageProducer producer = (MessageProducer) _session.createProducer(destination); + + _connection.start(); + //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths"); + final long startTime = System.currentTimeMillis(); + + for (int i = 0; i < messageCount; i++) + { + TextMessage msg = _session.createTextMessage(destination.getTopicName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths: " + i); + + //msg.setIntProperty("a",i % 2); + //msg.setIntProperty("b",i % 4); + + producer.send(msg); + } + _log.info("Finished sending " + messageCount + " messages"); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } + + private void createConnection(String host, int port, String clientID) throws AMQException, URLSyntaxException + { + _connection = new AMQConnection(host, port, "guest", "guest", + clientID, "/test"); + } + + /** + * + * @param args argument 1 if present specifies the name of the temporary queue to create. Leaving it blank + * means the server will allocate a name. + */ + public static void main(String[] args) throws URLSyntaxException + { + if (args.length == 0) + { + System.err.println("Usage: TestPublisher "); + } + try + { + int port = Integer.parseInt(args[1]); + InetAddress address = InetAddress.getLocalHost(); + String clientID = address.getHostName() + System.currentTimeMillis(); + TestPublisher client = new TestPublisher(args[0], port, clientID, args[2], Integer.parseInt(args[3])); + } + catch (UnknownHostException e) + { + e.printStackTrace(); + } + catch (AMQException e) + { + System.err.println("Error in client: " + e); + e.printStackTrace(); + } + + //System.exit(0); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java new file mode 100644 index 0000000000..450d9b3914 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java @@ -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. + * + */ +package org.apache.qpid.pubsub1; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.jms.Session; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Topic; +import java.net.InetAddress; + +public class TestSubscriber +{ + private static final Logger _logger = Logger.getLogger(TestSubscriber.class); + + private static class TestMessageListener implements MessageListener + { + private String _name; + + private int _expectedMessageCount; + + private int _messageCount; + + private long _startTime = 0; + + public TestMessageListener(String name, int expectedMessageCount) + { + _name = name; + _expectedMessageCount = expectedMessageCount; + } + + public void onMessage(javax.jms.Message message) + { + if (_messageCount++ == 0) + { + _startTime = System.currentTimeMillis(); + } + if (_logger.isInfoEnabled()) + { + _logger.info(_name + " got message '" + message + "'"); + } + if (_messageCount == _expectedMessageCount) + { + long totalTime = System.currentTimeMillis() - _startTime; + _logger.error(_name + ": Total time to receive " + _messageCount + " messages was " + + totalTime + "ms. Rate is " + (_messageCount/(totalTime/1000.0))); + } + if (_messageCount > _expectedMessageCount) + { + _logger.error("Oops! More messages received than expected (" + _messageCount + ")"); + } + } + } + + public static void main(String[] args) + { + _logger.info("Starting..."); + + if (args.length != 7) + { + System.out.println("Usage: host port username password virtual-path expectedMessageCount selector"); + System.exit(1); + } + try + { + InetAddress address = InetAddress.getLocalHost(); + AMQConnection con1 = new AMQConnection(args[0], Integer.parseInt(args[1]), args[2], args[3], + address.getHostName(), args[4]); + final Session session1 = con1.createSession(false, Session.AUTO_ACKNOWLEDGE); + + AMQConnection con2 = new AMQConnection(args[0], Integer.parseInt(args[1]), args[2], args[3], + address.getHostName(), args[4]); + final Session session2 = con2.createSession(false, Session.AUTO_ACKNOWLEDGE); + String selector = args[6]; + + final int expectedMessageCount = Integer.parseInt(args[5]); + _logger.info("Message selector is <" + selector + ">..."); + + Topic t = new AMQTopic(session1.getDefaultTopicExchangeName(), new AMQShortString("cbr")); + MessageConsumer consumer1 = session1.createConsumer(t, + 100, false, false, selector); + MessageConsumer consumer2 = session2.createConsumer(t, + 100, false, false, selector); + + consumer1.setMessageListener(new TestMessageListener("ML 1", expectedMessageCount)); + consumer2.setMessageListener(new TestMessageListener("ML 2", expectedMessageCount)); + con1.start(); + con2.start(); + } + catch (Throwable t) + { + System.err.println("Fatal error: " + t); + t.printStackTrace(); + } + + System.out.println("Waiting..."); + } +} + diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java new file mode 100644 index 0000000000..f59b36166a --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java @@ -0,0 +1,95 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.connection; + +import org.apache.qpid.AMQException; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.client.AMQConnection; +import org.apache.log4j.Logger; + +import junit.framework.TestCase; + +public class TestManyConnections extends TestCase +{ + private static final Logger _log = Logger.getLogger(TestManyConnections.class); + + private AMQConnection[] _connections; + + private void createConnection(int index, String brokerHosts, String clientID, String username, String password, + String vpath) throws AMQException, URLSyntaxException + { + _connections[index] = new AMQConnection(brokerHosts, username, password, + clientID, vpath); + } + + private void createConnections(int count) throws AMQException, URLSyntaxException + { + _connections = new AMQConnection[count]; + long startTime = System.currentTimeMillis(); + for (int i = 0; i < count; i++) + { + createConnection(i, "vm://:1", "myClient" + i, "guest", "guest", "test"); + } + long endTime = System.currentTimeMillis(); + _log.info("Time to create " + count + " connections: " + (endTime - startTime) + + "ms"); + } + + public void testCreate10Connections() throws AMQException, URLSyntaxException + { + createConnections(10); + } + + public void testCreate50Connections() throws AMQException, URLSyntaxException + { + createConnections(50); + } + + public void testCreate100Connections() throws AMQException, URLSyntaxException + { + createConnections(100); + } + + public void testCreate250Connections() throws AMQException, URLSyntaxException + { + createConnections(250); + } + + public void testCreate500Connections() throws AMQException, URLSyntaxException + { + createConnections(500); + } + + public void testCreate1000Connections() throws AMQException, URLSyntaxException + { + createConnections(1000); + } + + public void testCreate5000Connections() throws AMQException, URLSyntaxException + { + createConnections(5000); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TestManyConnections.class); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java new file mode 100644 index 0000000000..5ab5722146 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java @@ -0,0 +1,153 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.jndi; + +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; +import java.util.Properties; +import java.io.InputStream; + + +import junit.framework.TestCase; + +public class PropertiesFileInitialContextFactoryTest extends TestCase +{ + InitialContextFactory contextFactory; + Properties _properties; + Properties _fileProperties; + + protected void setUp() throws Exception + { + super.setUp(); + + //create simple set of hardcoded props + _properties = new Properties(); + _properties.put("java.naming.factory.initial", "org.apache.qpid.jndi.PropertiesFileInitialContextFactory"); + _properties.put("connectionfactory.local", "amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'"); + _properties.put("queue.MyQueue", "example.MyQueue"); + _properties.put("topic.ibmStocks", "stocks.nyse.ibm"); + _properties.put("destination.direct", "direct://amq.direct//directQueue"); + + //create properties from file as a more realistic test + _fileProperties = new Properties(); + ClassLoader cl = this.getClass().getClassLoader(); + InputStream is = cl.getResourceAsStream("org/apache/qpid/test/unit/jndi/example.properties"); + _fileProperties.load(is); + } + + /** + * Test using hardcoded properties + */ + public void testWithoutFile() + { + Context ctx = null; + + try + { + ctx = new InitialContext(_properties); + } + catch (NamingException ne) + { + fail("Error loading context:" + ne); + } + + checkPropertiesMatch(ctx, "Using hardcoded properties: "); + } + + /** + * Test using properties from example file + */ + public void testWithFile() + { + Context ctx = null; + + try + { + ctx = new InitialContext(_fileProperties); + } + catch (Exception e) + { + fail("Error loading context:" + e); + } + + checkPropertiesMatch(ctx, "Using properties from file: "); + } + + public void tearDown() + { + _properties = null; + _fileProperties = null; + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(PropertiesFileInitialContextFactoryTest.class); + } + + private void checkPropertiesMatch(Context ctx, String errorInfo) + { + try + { + AMQConnectionFactory cf = (AMQConnectionFactory) ctx.lookup("local"); + assertEquals("amqp://guest:guest@clientid/testpath?brokerlist='vm://:1'", cf.getConnectionURL().toString()); + } + catch (NamingException ne) + { + fail(errorInfo + "Unable to create Connection Factory:" + ne); + } + + try + { + AMQQueue queue = (AMQQueue) ctx.lookup("MyQueue"); + assertEquals("example.MyQueue", queue.getRoutingKey().toString()); + } + catch (NamingException ne) + { + fail(errorInfo + "Unable to create queue:" + ne); + } + + try + { + AMQTopic topic = (AMQTopic) ctx.lookup("ibmStocks"); + assertEquals("stocks.nyse.ibm", topic.getTopicName().toString()); + } + catch (Exception ne) + { + fail(errorInfo + "Unable to create topic:" + ne); + } + + try + { + AMQQueue direct = (AMQQueue) ctx.lookup("direct"); + assertEquals("directQueue", direct.getRoutingKey().toString()); + } + catch (NamingException ne) + { + fail(errorInfo + "Unable to create direct destination:" + ne); + } + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties new file mode 100644 index 0000000000..ea9dc5ae0e --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties @@ -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. + +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# use the following property to configure the default connector +#java.naming.provider.url - ignored. + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.local = amqp://guest:guest@clientid/testpath?brokerlist='vm://:1' + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.ibmStocks = stocks.nyse.ibm + +# Register an AMQP destination in JNDI +# NOTE: Qpid currently only supports direct,topics and headers +# destination.[jniName] = [BindingURL] +destination.direct = direct://amq.direct//directQueue diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Config.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Config.java new file mode 100644 index 0000000000..bb740f9094 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Config.java @@ -0,0 +1,243 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.topic; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.config.ConnectorConfig; +import org.apache.qpid.config.ConnectionFactoryInitialiser; +import org.apache.qpid.config.Connector; +import org.apache.qpid.config.AbstractConfig; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; + +class Config extends AbstractConfig implements ConnectorConfig +{ + + private String host = "localhost"; + private int port = 5672; + private String factory = null; + + private int payload = 256; + private int messages = 1000; + private int clients = 1; + private int batch = 1; + private long delay = 1; + private int warmup; + private int ackMode= AMQSession.NO_ACKNOWLEDGE; + private String clientId; + private String subscriptionId; + private boolean persistent; + + public Config() + { + } + + int getAckMode() + { + return ackMode; + } + + void setPayload(int payload) + { + this.payload = payload; + } + + int getPayload() + { + return payload; + } + + void setClients(int clients) + { + this.clients = clients; + } + + int getClients() + { + return clients; + } + + void setMessages(int messages) + { + this.messages = messages; + } + + int getMessages() + { + return messages; + } + + public String getHost() + { + return host; + } + + public void setHost(String host) + { + this.host = host; + } + + public int getPort() + { + return port; + } + + public String getFactory() + { + return factory; + } + + public void setPort(int port) + { + this.port = port; + } + + int getBatch() + { + return batch; + } + + void setBatch(int batch) + { + this.batch = batch; + } + + int getWarmup() + { + return warmup; + } + + void setWarmup(int warmup) + { + this.warmup = warmup; + } + + public long getDelay() + { + return delay; + } + + public void setDelay(long delay) + { + this.delay = delay; + } + + String getClientId() + { + return clientId; + } + + String getSubscriptionId() + { + return subscriptionId; + } + + boolean usePersistentMessages() + { + return persistent; + } + + public void setOption(String key, String value) + { + if("-host".equalsIgnoreCase(key)) + { + setHost(value); + } + else if("-port".equalsIgnoreCase(key)) + { + try + { + setPort(Integer.parseInt(value)); + } + catch(NumberFormatException e) + { + throw new RuntimeException("Bad port number: " + value); + } + } + else if("-payload".equalsIgnoreCase(key)) + { + setPayload(parseInt("Bad payload size", value)); + } + else if("-messages".equalsIgnoreCase(key)) + { + setMessages(parseInt("Bad message count", value)); + } + else if("-clients".equalsIgnoreCase(key)) + { + setClients(parseInt("Bad client count", value)); + } + else if("-batch".equalsIgnoreCase(key)) + { + setBatch(parseInt("Bad batch count", value)); + } + else if("-delay".equalsIgnoreCase(key)) + { + setDelay(parseLong("Bad batch delay", value)); + } + else if("-warmup".equalsIgnoreCase(key)) + { + setWarmup(parseInt("Bad warmup count", value)); + } + else if("-ack".equalsIgnoreCase(key)) + { + ackMode = parseInt("Bad ack mode", value); + } + else if("-factory".equalsIgnoreCase(key)) + { + factory = value; + } + else if("-clientId".equalsIgnoreCase(key)) + { + clientId = value; + } + else if("-subscriptionId".equalsIgnoreCase(key)) + { + subscriptionId = value; + } + else if("-persistent".equalsIgnoreCase(key)) + { + persistent = "true".equalsIgnoreCase(value); + } + else + { + System.out.println("Ignoring unrecognised option: " + key); + } + } + + static String getAckModeDescription(int ackMode) + { + switch(ackMode) + { + case AMQSession.NO_ACKNOWLEDGE: return "NO_ACKNOWLEDGE"; + case AMQSession.AUTO_ACKNOWLEDGE: return "AUTO_ACKNOWLEDGE"; + case AMQSession.CLIENT_ACKNOWLEDGE: return "CLIENT_ACKNOWLEDGE"; + case AMQSession.DUPS_OK_ACKNOWLEDGE: return "DUPS_OK_ACKNOWELDGE"; + case AMQSession.PRE_ACKNOWLEDGE: return "PRE_ACKNOWLEDGE"; + } + return "AckMode=" + ackMode; + } + + public Connection createConnection() throws Exception + { + return new Connector().createConnection(this); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java new file mode 100644 index 0000000000..47c608cfe4 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java @@ -0,0 +1,141 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.topic; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; + +public class Listener implements MessageListener +{ + private final Connection _connection; + private final MessageProducer _controller; + private final javax.jms.Session _session; + private final MessageFactory _factory; + private boolean init; + private int count; + private long start; + + Listener(Connection connection, int ackMode) throws Exception + { + this(connection, ackMode, null); + } + + Listener(Connection connection, int ackMode, String name) throws Exception + { + _connection = connection; + _session = connection.createSession(false, ackMode); + _factory = new MessageFactory(_session); + + //register for events + if(name == null) + { + _factory.createTopicConsumer().setMessageListener(this); + } + else + { + _factory.createDurableTopicConsumer(name).setMessageListener(this); + } + + _connection.start(); + + _controller = _factory.createControlPublisher(); + System.out.println("Waiting for messages " + + Config.getAckModeDescription(ackMode) + + (name == null ? "" : " (subscribed with name " + name + " and client id " + connection.getClientID() + ")") + + "..."); + + } + + private void shutdown() + { + try + { + _session.close(); + _connection.stop(); + _connection.close(); + } + catch(Exception e) + { + e.printStackTrace(System.out); + } + } + + private void report() + { + try + { + String msg = getReport(); + _controller.send(_factory.createReportResponseMessage(msg)); + System.out.println("Sent report: " + msg); + } + catch(Exception e) + { + e.printStackTrace(System.out); + } + } + + private String getReport() + { + long time = (System.currentTimeMillis() - start); + return "Received " + count + " in " + time + "ms"; + } + + public void onMessage(Message message) + { + if(!init) + { + start = System.currentTimeMillis(); + count = 0; + init = true; + } + + if(_factory.isShutdown(message)) + { + shutdown(); + } + else if(_factory.isReport(message)) + { + //send a report: + report(); + init = false; + } + else if (++count % 100 == 0) + { + System.out.println("Received " + count + " messages."); + } + } + + public static void main(String[] argv) throws Exception + { + Config config = new Config(); + config.setOptions(argv); + + Connection con = config.createConnection(); + if(config.getClientId() != null) + { + con.setClientID(config.getClientId()); + } + new Listener(con, config.getAckMode(), config.getSubscriptionId()); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java new file mode 100644 index 0000000000..39d64069d1 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.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.topic; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; + +import javax.jms.*; + +/** + */ +class MessageFactory +{ + private static final char[] DATA = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + + private final Session _session; + private final Topic _topic; + private final Topic _control; + private final byte[] _payload; + + + MessageFactory(Session session) throws JMSException + { + this(session, 256); + } + + MessageFactory(Session session, int size) throws JMSException + { + _session = session; + if(session instanceof AMQSession) + { + _topic = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(),new AMQShortString("topictest.messages")); + _control = new AMQTopic(((AMQSession)session).getDefaultTopicExchangeName(),new AMQShortString("topictest.control")); + } + else + { + _topic = session.createTopic("topictest.messages"); + _control = session.createTopic("topictest.control"); + } + _payload = new byte[size]; + + for(int i = 0; i < size; i++) + { + _payload[i] = (byte) DATA[i % DATA.length]; + } + } + + Topic getTopic() + { + return _topic; + } + + Message createEventMessage() throws JMSException + { + BytesMessage msg = _session.createBytesMessage(); + msg.writeBytes(_payload); + return msg; + } + + Message createShutdownMessage() throws JMSException + { + return _session.createTextMessage("SHUTDOWN"); + } + + Message createReportRequestMessage() throws JMSException + { + return _session.createTextMessage("REPORT"); + } + + Message createReportResponseMessage(String msg) throws JMSException + { + return _session.createTextMessage(msg); + } + + boolean isShutdown(Message m) + { + return checkText(m, "SHUTDOWN"); + } + + boolean isReport(Message m) + { + return checkText(m, "REPORT"); + } + + Object getReport(Message m) + { + try + { + return ((TextMessage) m).getText(); + } + catch (JMSException e) + { + e.printStackTrace(System.out); + return e.toString(); + } + } + + MessageConsumer createTopicConsumer() throws Exception + { + return _session.createConsumer(_topic); + } + + MessageConsumer createDurableTopicConsumer(String name) throws Exception + { + return _session.createDurableSubscriber(_topic, name); + } + + MessageConsumer createControlConsumer() throws Exception + { + return _session.createConsumer(_control); + } + + MessageProducer createTopicPublisher() throws Exception + { + return _session.createProducer(_topic); + } + + MessageProducer createControlPublisher() throws Exception + { + return _session.createProducer(_control); + } + + private static boolean checkText(Message m, String s) + { + try + { + return m instanceof TextMessage && ((TextMessage) m).getText().equals(s); + } + catch (JMSException e) + { + e.printStackTrace(System.out); + return false; + } + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java new file mode 100644 index 0000000000..d788029ee9 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.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.topic; + +import javax.jms.*; + +public class Publisher implements MessageListener +{ + private final Object _lock = new Object(); + private final Connection _connection; + private final Session _session; + private final MessageFactory _factory; + private final MessageProducer _publisher; + private int _count; + + Publisher(Connection connection, int size, int ackMode, boolean persistent) throws Exception + { + _connection = connection; + _session = _connection.createSession(false, ackMode); + _factory = new MessageFactory(_session, size); + _publisher = _factory.createTopicPublisher(); + _publisher.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + System.out.println("Publishing " + (persistent ? "persistent" : "non-persistent") + " messages of " + size + " bytes, " + Config.getAckModeDescription(ackMode) + "."); + } + + private void test(Config config) throws Exception + { + test(config.getBatch(), config.getDelay(), config.getMessages(), config.getClients(), config.getWarmup()); + } + + private void test(int batches, long delay, int msgCount, int consumerCount, int warmup) throws Exception + { + _factory.createControlConsumer().setMessageListener(this); + _connection.start(); + + if(warmup > 0) + { + System.out.println("Runing warmup (" + warmup + " msgs)"); + long time = batch(warmup, consumerCount); + System.out.println("Warmup completed in " + time + "ms"); + } + + long[] times = new long[batches]; + for(int i = 0; i < batches; i++) + { + if(i > 0) Thread.sleep(delay*1000); + times[i] = batch(msgCount, consumerCount); + System.out.println("Batch " + (i+1) + " of " + batches + " completed in " + times[i] + " ms."); + } + + long min = min(times); + long max = max(times); + System.out.println("min: " + min + ", max: " + max + " avg: " + avg(times, min, max)); + + //request shutdown + _publisher.send(_factory.createShutdownMessage()); + + _connection.stop(); + _connection.close(); + } + + private long batch(int msgCount, int consumerCount) throws Exception + { + _count = consumerCount; + long start = System.currentTimeMillis(); + publish(msgCount); + waitForCompletion(consumerCount); + return System.currentTimeMillis() - start; + } + + private void publish(int count) throws Exception + { + + //send events + for (int i = 0; i < count; i++) + { + _publisher.send(_factory.createEventMessage()); + if ((i + 1) % 100 == 0) + { + System.out.println("Sent " + (i + 1) + " messages"); + } + } + + //request report + _publisher.send(_factory.createReportRequestMessage()); + } + + private void waitForCompletion(int consumers) throws Exception + { + System.out.println("Waiting for completion..."); + synchronized (_lock) + { + while (_count > 0) + { + _lock.wait(); + } + } + } + + + public void onMessage(Message message) + { + System.out.println("Received report " + _factory.getReport(message) + " " + --_count + " remaining"); + if (_count == 0) + { + synchronized (_lock) + { + _lock.notify(); + } + } + } + + static long min(long[] times) + { + long min = times.length > 0 ? times[0] : 0; + for(int i = 0; i < times.length; i++) + { + min = Math.min(min, times[i]); + } + return min; + } + + static long max(long[] times) + { + long max = times.length > 0 ? times[0] : 0; + for(int i = 0; i < times.length; i++) + { + max = Math.max(max, times[i]); + } + return max; + } + + static long avg(long[] times, long min, long max) + { + long sum = 0; + for(int i = 0; i < times.length; i++) + { + sum += times[i]; + } + sum -= min; + sum -= max; + + return (sum / (times.length - 2)); + } + + public static void main(String[] argv) throws Exception + { + Config config = new Config(); + config.setOptions(argv); + + Connection con = config.createConnection(); + int size = config.getPayload(); + int ackMode = config.getAckMode(); + boolean persistent = config.usePersistentMessages(); + new Publisher(con, size, ackMode, persistent).test(config); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java new file mode 100644 index 0000000000..bd104e5407 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java @@ -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. + * + */ +package org.apache.qpid.transacted; + +import org.apache.qpid.config.ConnectorConfig; +import org.apache.qpid.config.AbstractConfig; +import org.apache.qpid.config.Connector; + +import javax.jms.Connection; + +class Config extends AbstractConfig implements ConnectorConfig +{ + private String host = "localhost"; + private int port = 5672; + private String factory; + private boolean echo; + private int batch = 100; + private boolean persistent = true; + + Config(String[] argv) + { + setOptions(argv); + } + + Connection createConnection() throws Exception + { + return new Connector().createConnection(this); + } + + public boolean isEchoOn() + { + return echo; + } + + public boolean usePersistentMessages() + { + return persistent; + } + + public int getBatchSize() + { + return batch; + } + + public String getHost() + { + return host; + } + + public int getPort() + { + return port; + } + + public String getFactory() + { + return factory; + } + + public void setOption(String key, String value) + { + if("-host".equalsIgnoreCase(key)) + { + host = value; + } + else if("-port".equalsIgnoreCase(key)) + { + port = parseInt("Bad port number", value); + } + else if("-factory".equalsIgnoreCase(key)) + { + factory = value; + } + else if("-echo".equalsIgnoreCase(key)) + { + echo = "true".equalsIgnoreCase(value); + } + else if("-persistent".equalsIgnoreCase(key)) + { + persistent = "true".equalsIgnoreCase(value); + } + else if("-batch".equalsIgnoreCase(key)) + { + batch = parseInt("Bad batch size", value); + } + else + { + System.out.println("Ignoring nrecognised option " + key); + } + } + +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java new file mode 100644 index 0000000000..8f15bf089e --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java @@ -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. + * + */ +package org.apache.qpid.transacted; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.client.AMQQueue; + +import javax.jms.Connection; +import javax.jms.JMSException; +import java.util.Arrays; + +public class Ping +{ + public static void main(String[] argv) throws Exception + { + Config config = new Config(argv); + Connection con = config.createConnection(); + con.setClientID("ping"); + new Relay(new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping")), new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("pong")), con, + config.isEchoOn(), + config.getBatchSize(), + config.usePersistentMessages()).start(); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java new file mode 100644 index 0000000000..f4f4b20d7c --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java @@ -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. + * + */ +package org.apache.qpid.transacted; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.client.AMQQueue; + +import javax.jms.Connection; +import javax.jms.JMSException; + +public class Pong +{ + public static void main(String[] argv) throws Exception + { + Config config = new Config(argv); + Connection con = config.createConnection(); + con.setClientID("pong"); + new Relay(new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("pong")), new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping")), con, + config.isEchoOn(), + config.getBatchSize(), + config.usePersistentMessages()).start(); + + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java new file mode 100644 index 0000000000..cede95e5f0 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java @@ -0,0 +1,127 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transacted; + +import org.apache.qpid.client.AMQSession; + +import javax.jms.MessageProducer; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.Destination; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.TextMessage; +import javax.jms.DeliveryMode; + +class Relay implements Runnable +{ + private final Connection _con; + private final Session _session; + private final MessageConsumer _src; + private final MessageProducer _dest; + private final int _batch; + private final boolean _echo; + private int _counter; + private long start; + private boolean _running; + + Relay(Destination src, Destination dest, Connection con) throws JMSException + { + this(src, dest, con, false, 100, true); + } + + Relay(Destination src, Destination dest, Connection con, boolean echo, int batch, boolean persistent) throws JMSException + { + _echo = echo; + _batch = batch; + _con = con; + _session = con.createSession(true, AMQSession.NO_ACKNOWLEDGE); + _src = _session.createConsumer(src); + _dest = _session.createProducer(dest); + _dest.setDeliveryMode(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + + } + + public void run() + { + start = System.currentTimeMillis(); + try{ + while(true) relay(); + } + catch(JMSException e) + { + e.printStackTrace(); + } + try + { + _session.close(); + } + catch (JMSException e) + { + e.printStackTrace(); + } + } + + void relay() throws JMSException + { + _dest.send(relay(_src.receive())); + _session.commit(); + } + + Message relay(Message in) throws JMSException + { + if(!_running) + { + System.out.println(_con.getClientID() + " started."); + _running = true; + } + if(++_counter % _batch == 0) + { + long time = System.currentTimeMillis() - start; + System.out.println(_batch + " iterations performed in " + time + " ms"); + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + start = System.currentTimeMillis(); + } + if(_echo) + { + System.out.println("Received: " + ((TextMessage) in).getText()); + } + return _session.createTextMessage(_con.getClientID() + _counter); + } + + void start() throws InterruptedException, JMSException + { + Thread runner = new Thread(this); + runner.start(); + _con.start(); + System.out.println(_con.getClientID() + " waiting..."); + runner.join(); + _con.close(); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java new file mode 100644 index 0000000000..de718d828a --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.transacted; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.client.AMQQueue; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Session; + +public class Start +{ + public static void main(String[] argv) throws Exception + { + Connection con = new Config(argv).createConnection(); + AMQQueue ping = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString("ping")); + Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + session.createProducer(ping).send(session.createTextMessage("start")); + session.close(); + con.close(); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java new file mode 100644 index 0000000000..71d806b338 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java @@ -0,0 +1,151 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.weblogic; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; + +import javax.jms.*; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.naming.Context; +import java.net.InetAddress; +import java.util.Hashtable; + +public class ServiceProvider +{ + private static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory"; + private static final String JMS_FACTORY = "transientJMSConnectionFactory"; + + private static final Logger _logger = Logger.getLogger(ServiceProvider.class); + + private static MessageProducer _destinationProducer; + + private static Queue _destinationQ; + + public static void main(String[] args) + { + _logger.info("Starting..."); + + if (args.length != 2) + { + System.out.println("Usage: "); + System.exit(1); + } + try + { + String url = args[0]; + String receiveQueue = args[1]; + + final InitialContext ctx = getInitialContext(url); + + QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY); + QueueConnection qcon = qconFactory.createQueueConnection(); + final QueueSession qsession = qcon.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue receiveQ = (Queue) ctx.lookup(receiveQueue); + + _logger.info("Service (queue) name is '" + receiveQ + "'..."); + + String selector = (args.length > 2 && args[2] != null && args[2].length() > 1) ? args[2] : null; + + _logger.info("Message selector is <" + selector + ">..."); + + MessageConsumer consumer = qsession.createConsumer(receiveQ, selector); + + consumer.setMessageListener(new MessageListener() + { + private int _messageCount; + + public void onMessage(javax.jms.Message message) + { + //_logger.info("Got message '" + message + "'"); + + TextMessage tm = (TextMessage) message; + + try + { + Queue responseQueue = (Queue)tm.getJMSReplyTo(); + if (!responseQueue.equals(_destinationQ)) + { + _destinationQ = responseQueue; + _logger.info("Creating destination for " + responseQueue); + + try + { + _destinationProducer = qsession.createProducer(_destinationQ); + _destinationProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + catch (JMSException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + _messageCount++; + if (_messageCount % 1000 == 0) + { + _logger.info("Received message total: " + _messageCount); + _logger.info("Sending response to '" + responseQueue + "'"); + } + + String payload = "This is a response: sing together: 'Mahnah mahnah...'" + tm.getText(); + TextMessage msg = qsession.createTextMessage(payload); + if (tm.propertyExists("timeSent")) + { + _logger.info("timeSent property set on message"); + final long timeSent = tm.getLongProperty("timeSent"); + msg.setLongProperty("timeSent", timeSent); + _logger.info("time taken to go from service request to provider is: " + (System.currentTimeMillis() - timeSent)); + } + _destinationProducer.send(msg); + if (_messageCount % 1000 == 0) + { + tm.acknowledge(); + _logger.info("Sent response to '" + responseQueue + "'"); + } + } + catch (JMSException e) + { + _logger.error("Error sending message: " + e, e); + } + } + }); + qcon.start(); + } + catch (Throwable t) + { + System.err.println("Fatal error: " + t); + t.printStackTrace(); + } + + + System.out.println("Waiting..."); + } + + private static InitialContext getInitialContext(String url) throws NamingException + { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); + env.put(Context.PROVIDER_URL, url); + return new InitialContext(env); + } +} diff --git a/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java new file mode 100644 index 0000000000..2f64a1dde5 --- /dev/null +++ b/RC6/qpid/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.weblogic; + +import org.apache.log4j.Logger; + +import javax.jms.*; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Hashtable; + +/** + * Created by IntelliJ IDEA. + * User: U806869 + * Date: 28-May-2005 + * Time: 21:54:51 + * To change this template use File | Settings | File Templates. + */ +public class ServiceRequestingClient +{ + private static final Logger _log = Logger.getLogger(ServiceRequestingClient.class); + private static final String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory"; + private static final String JMS_FACTORY = "transientJMSConnectionFactory"; + + private static class CallbackHandler implements MessageListener + { + private int _expectedMessageCount; + + private int _actualMessageCount; + + private long _startTime; + + private long _averageLatency; + + public CallbackHandler(int expectedMessageCount, long startTime) + { + _expectedMessageCount = expectedMessageCount; + _startTime = startTime; + } + + public void onMessage(Message m) + { + if (_log.isDebugEnabled()) + { + _log.debug("Message received: " + m); + } + try + { + if (m.propertyExists("timeSent")) + { + long timeSent = m.getLongProperty("timeSent"); + long now = System.currentTimeMillis(); + if (_averageLatency == 0) + { + _averageLatency = now - timeSent; + _log.info("Latency " + _averageLatency); + } + else + { + _log.info("Individual latency: " + (now-timeSent)); + _averageLatency = (_averageLatency + (now - timeSent))/2; + _log.info("Average latency now: " + _averageLatency); + } + } + } + catch (JMSException e) + { + _log.error("Could not calculate latency"); + } + + _actualMessageCount++; + if (_actualMessageCount%1000 == 0) + { + try + { + m.acknowledge(); + } + catch (JMSException e) + { + _log.error("Error acknowledging message"); + } + _log.info("Received message count: " + _actualMessageCount); + } + /*if (!"henson".equals(m.toString())) + { + _log.error("Message response not correct: expected 'henson' but got " + m.toString()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("Message " + m + " received"); + } + else + { + _log.info("Message received"); + } + } */ + + if (_actualMessageCount == _expectedMessageCount) + { + long timeTaken = System.currentTimeMillis() - _startTime; + System.out.println("Total time taken to receive " + _expectedMessageCount+ " messages was " + + timeTaken + "ms, equivalent to " + + (_expectedMessageCount/(timeTaken/1000.0)) + " messages per second"); + System.out.println("Average latency is: " + _averageLatency); + } + } + } + + public static void main(String[] args) throws Exception + { + if (args.length != 3) + { + System.out.println("Usage: IXPublisher will publish count messages to "); + System.out.println("queue sendQueue and waits for a response on a temp queue"); + System.exit(1); + } + + String url = args[0]; + String sendQueue = args[1]; + int messageCount = Integer.parseInt(args[2]); + + InitialContext ctx = getInitialContext(url); + + QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY); + QueueConnection qcon = qconFactory.createQueueConnection(); + QueueSession qsession = qcon.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue sendQ = (Queue) ctx.lookup(sendQueue); + Queue receiveQ = qsession.createTemporaryQueue(); + QueueSender qsender = qsession.createSender(sendQ); + qsender.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + _log.debug("Queue sender created for service queue " + sendQ); + + javax.jms.MessageConsumer messageConsumer = (javax.jms.MessageConsumer) qsession.createConsumer(receiveQ); + + //TextMessage msg = _session.createTextMessage(tempDestination.getQueueName() + "/Presented to in conjunction with Mahnah Mahnah and the Snowths"); + final long startTime = System.currentTimeMillis(); + + messageConsumer.setMessageListener(new CallbackHandler(messageCount, startTime)); + qcon.start(); + for (int i = 0; i < messageCount; i++) + { + TextMessage msg = qsession.createTextMessage("/Presented to in conjunction with Mahnah Mahnah and the Snowths:" + i); + msg.setJMSReplyTo(receiveQ); + if (i%1000 == 0) + { + long timeNow = System.currentTimeMillis(); + msg.setLongProperty("timeSent", timeNow); + } + qsender.send(msg); + } + + new Thread("foo").start(); + //qsession.close(); + //qcon.close(); + } + + private static InitialContext getInitialContext(String url) throws NamingException + { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); + env.put(Context.PROVIDER_URL, url); + return new InitialContext(env); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java b/RC6/qpid/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java new file mode 100644 index 0000000000..5323ad28bf --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java @@ -0,0 +1,125 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.vmpipe.support; + +import org.apache.mina.common.IdleStatus; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * This file is a patch to override MINA, because of the IdentityHashMap bug. Workaround to be supplied in MINA 1.0.7. + * This patched file will be removed once upgraded onto a newer MINA. + * + * Dectects idle sessions and fires sessionIdle events to them. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + */ +public class VmPipeIdleStatusChecker +{ + private static final VmPipeIdleStatusChecker INSTANCE = new VmPipeIdleStatusChecker(); + + public static VmPipeIdleStatusChecker getInstance() + { + return INSTANCE; + } + + private final Map sessions = new HashMap(); // will use as a set + + private final Worker worker = new Worker(); + + private VmPipeIdleStatusChecker() + { + worker.start(); + } + + public void addSession(VmPipeSessionImpl session) + { + synchronized (sessions) + { + sessions.put(session, session); + } + } + + private class Worker extends Thread + { + private Worker() + { + super("VmPipeIdleStatusChecker"); + setDaemon(true); + } + + public void run() + { + for (;;) + { + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { } + + long currentTime = System.currentTimeMillis(); + + synchronized (sessions) + { + Iterator it = sessions.keySet().iterator(); + while (it.hasNext()) + { + VmPipeSessionImpl session = (VmPipeSessionImpl) it.next(); + if (!session.isConnected()) + { + it.remove(); + } + else + { + notifyIdleSession(session, currentTime); + } + } + } + } + } + } + + private void notifyIdleSession(VmPipeSessionImpl session, long currentTime) + { + notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE), IdleStatus.BOTH_IDLE, + Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE))); + notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.READER_IDLE), IdleStatus.READER_IDLE, + Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE))); + notifyIdleSession0(session, currentTime, session.getIdleTimeInMillis(IdleStatus.WRITER_IDLE), IdleStatus.WRITER_IDLE, + Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE))); + } + + private void notifyIdleSession0(VmPipeSessionImpl session, long currentTime, long idleTime, IdleStatus status, + long lastIoTime) + { + if ((idleTime > 0) && (lastIoTime != 0) && ((currentTime - lastIoTime) >= idleTime)) + { + session.increaseIdleCount(status); + session.getFilterChain().fireSessionIdle(session, status); + } + } + +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java new file mode 100644 index 0000000000..7789f87ace --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/AMQQueueTest.java @@ -0,0 +1,42 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.framing.AMQShortString; + +import junit.framework.TestCase; + +public class AMQQueueTest extends TestCase +{ + AMQShortString exchange = new AMQShortString("test.exchange"); + AMQShortString routingkey = new AMQShortString("test-route"); + AMQShortString qname = new AMQShortString("test-queue"); + AMQShortString[] oneBinding = new AMQShortString[]{new AMQShortString("bindingA")}; + AMQShortString[] bindings = new AMQShortString[]{new AMQShortString("bindingB"), + new AMQShortString("bindingC")}; + + public void testToURLNoBindings() + { + AMQQueue dest = new AMQQueue(exchange, routingkey, qname); + String url = dest.toURL(); + assertEquals("direct://test.exchange/test-route/test-queue?routingkey='test-route'", url); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java new file mode 100644 index 0000000000..ce79080e97 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/MockAMQConnection.java @@ -0,0 +1,94 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.url.URLSyntaxException; + +import java.io.IOException; + +public class MockAMQConnection extends AMQConnection +{ + public MockAMQConnection(String broker, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + super(broker, username, password, clientName, virtualHost); + } + + public MockAMQConnection(String broker, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(broker, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String host, int port, String username, String password, String clientName, String virtualHost) + throws AMQException, URLSyntaxException + { + super(host, port, username, password, clientName, virtualHost); + } + + public MockAMQConnection(String host, int port, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(host, port, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName, String virtualHost, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(host, port, useSSL, username, password, clientName, virtualHost, sslConfig); + } + + public MockAMQConnection(String connection) + throws AMQException, URLSyntaxException + { + super(connection); + } + + public MockAMQConnection(String connection, SSLConfiguration sslConfig) + throws AMQException, URLSyntaxException + { + super(connection, sslConfig); + } + + public MockAMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) + throws AMQException + { + super(connectionURL, sslConfig); + } + + protected MockAMQConnection(String username, String password, String clientName, String virtualHost) + { + super(username, password, clientName, virtualHost); + } + + @Override + public ProtocolVersion makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + { + _connected = true; + _protocolHandler.getStateManager().changeState(AMQState.CONNECTION_OPEN); + return null; + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java new file mode 100644 index 0000000000..7ee991b63c --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.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.client.message; + +import javax.jms.JMSException; + +public class TestMessageHelper +{ + public static JMSTextMessage newJMSTextMessage() throws JMSException + { + return new JMSTextMessage(AMQMessageDelegateFactory.FACTORY_0_8); + } + + public static JMSBytesMessage newJMSBytesMessage() throws JMSException + { + return new JMSBytesMessage(AMQMessageDelegateFactory.FACTORY_0_8); + } + + public static JMSMapMessage newJMSMapMessage() throws JMSException + { + return new JMSMapMessage(AMQMessageDelegateFactory.FACTORY_0_8); + } + + public static JMSStreamMessage newJMSStreamMessage() + { + return new JMSStreamMessage(AMQMessageDelegateFactory.FACTORY_0_8); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java new file mode 100644 index 0000000000..10ec220d9e --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/AMQProtocolHandlerTest.java @@ -0,0 +1,297 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import junit.framework.TestCase; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.amqp_8_0.BasicRecoverOkBodyImpl; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.client.MockAMQConnection; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.state.AMQState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This is a test address QPID-1431 where frame listeners would fail to be notified of an incomming exception. + * + * Currently we do checks at the Session level to ensure that the connection/session are open. However, it is possible + * for the connection to close AFTER this check has been performed. + * + * Performing a similar check at the frameListener level in AMQProtocolHandler makes most sence as this will prevent + * listening when there can be no returning frames. + * + * With the correction in place it also means that the new listener will either make it on to the list for notification + * or it will be notified of any existing exception due to the connection being closed. + * + * There may still be an issue in this space if the client utilises a second thread to close the session as there will + * be no exception set to throw and so the wait will occur. That said when the session is closed the framelisteners + * should be notified. Not sure this is tested. + */ +public class AMQProtocolHandlerTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQProtocolHandlerTest.class); + + // The handler to test + AMQProtocolHandler _handler; + + // A frame to block upon whilst waiting the exception + AMQFrame _blockFrame; + + // Latch to know when the listener receives an exception + private CountDownLatch _handleCountDown; + // The listener that will receive an exception + BlockToAccessFrameListener _listener; + + @Override + public void setUp() throws Exception + { + //Create a new ProtocolHandler with a fake connection. + _handler = new AMQProtocolHandler(new MockAMQConnection("amqp://guest:guest@client/test?brokerlist='vm://:1'")); + + _handler.sessionCreated(new MockIoSession()); + + AMQBody body = BasicRecoverOkBodyImpl.getFactory().newInstance(null, 1); + _blockFrame = new AMQFrame(0, body); + + _handleCountDown = new CountDownLatch(1); + + _logger.info("Creating _Listener that should also receive the thrown exception."); + _listener = new BlockToAccessFrameListener(1); + } + + /** + * There are two paths based on the type of exception thrown. + * + * This tests that when an AMQException is thrown we get the same type of AMQException back with the real exception + * wrapped as the cause. + * + * @throws InterruptedException - if we are unable to wait for the test signals + */ + public void testFrameListenerUpdateWithAMQException() throws InterruptedException + { + AMQException trigger = new AMQAuthenticationException(AMQConstant.ACCESS_REFUSED, + "AMQPHTest", new RuntimeException()); + + performWithException(trigger); + + + AMQException receivedException = (AMQException) _listener.getReceivedException(); + + assertEquals("Return exception was not the expected type", + AMQAuthenticationException.class, receivedException.getClass()); + + assertEquals("The _Listener did not receive the correct error code", + trigger.getErrorCode(), receivedException.getErrorCode()); + } + + /** + * There are two paths based on the type of exception thrown. + * + * This tests that when a generic Exception is thrown we get the exception back wrapped in a AMQException + * as the cause. + * @throws InterruptedException - if we are unable to wait for the test signals + */ + public void testFrameListenerUpdateWithException() throws InterruptedException + { + + Exception trigger = new Exception(new RuntimeException()); + + performWithException(trigger); + + assertEquals("The _Listener did not receive the correct error code", + AMQConstant.INTERNAL_ERROR, ((AMQException)_listener.getReceivedException()).getErrorCode()); + } + + /** + * This is the main test method for both test cases. + * + * What occurs is that we create a new thread that will block (<30s[DEFAULT]) for a frame or exception to occur . + * + * We use a CountDownLatch to ensure that the new thread is running before we then yield and sleep to help ensure + * the new thread has entered the synchronized block in the writeCommandFrameAndWaitForReply. + * + * We can then ack like an the incomming exception handler in (ConnectionCloseMethodHandler). + * + * We fire the error to the stateManager, which in this case will recored the error as there are no state listeners. + * + * We then set the connection to be closed, as we would normally close the socket at this point. + * + * Then fire the exception in to any frameListeners. + * + * The blocked listener (created above) when receiving the error simulates the user by creating a new request to + * block for a frame. + * + * This request should fail. Prior to the fix this will fail with a NPE as we are attempting to use a null listener + * in the writeCommand.... call L:268. + * + * This highlights that the listener would be added dispite there being a pending error state that the listener will + * miss as it is not currently part of the _frameListeners set that is being notified by the iterator. + * + * The method waits to ensure that an exception is received before returning. + * + * The calling methods validate that exception that was received based on the one they sent in. + * + * @param trigger The exception to throw through the handler + */ + private void performWithException(Exception trigger) throws InterruptedException + { + + final CountDownLatch callingWriteCommand = new CountDownLatch(1); + + //Set an initial listener that will allow us to create a new blocking method + new Thread(new Runnable() + { + public void run() + { + + try + { + + _logger.info("At initial block, signalling to fire new exception"); + callingWriteCommand.countDown(); + + _handler.writeCommandFrameAndWaitForReply(_blockFrame, _listener); + } + catch (Exception e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + }).start(); + + _logger.info("Waiting for 'initial block' to start "); + if (!callingWriteCommand.await(1000, TimeUnit.MILLISECONDS)) + { + fail("Failed to start new thread to block for frame"); + } + + // Do what we can to ensure that this thread does not continue before the above thread has hit the synchronized + // block in the writeCommandFrameAndWaitForReply + Thread.yield(); + Thread.sleep(1000); + + _logger.info("Firing Erorr through state manager. There should be not state waiters here."); + _handler.getStateManager().error(trigger); + + _logger.info("Setting state to be CONNECTION_CLOSED."); + try + { + _handler.getStateManager().changeState(AMQState.CONNECTION_CLOSED); + } + catch (AMQException e) + { + _logger.error("Unable to change the state to closed.", e); + fail("Unable to change the state to closed due to :"+e.getMessage()); + } + + _logger.info("Firing exception"); + _handler.propagateExceptionToFrameListeners(trigger); + + _logger.info("Awaiting notifcation from handler that exception arrived."); + + if (!_handleCountDown.await(2000, TimeUnit.MILLISECONDS)) + { + fail("Failed to handle exception and timeout has not occured"); + } + + + assertNotNull("The _Listener did not receive the exception", _listener.getReceivedException()); + + assertTrue("Received exception not an AMQException", + _listener.getReceivedException() instanceof AMQException); + + AMQException receivedException = (AMQException) _listener.getReceivedException(); + + assertTrue("The _Listener did not receive the correct message", + receivedException.getMessage().startsWith(trigger.getMessage())); + + + assertEquals("The _Listener did not receive the correct cause", + trigger, receivedException.getCause()); + + assertEquals("The _Listener did not receive the correct sub cause", + trigger.getCause(), receivedException.getCause().getCause()); + + } + + class BlockToAccessFrameListener extends BlockingMethodFrameListener + { + private Exception _receivedException = null; + + /** + * Creates a new method listener, that filters incoming method to just those that match the specified channel id. + * + * @param channelId The channel id to filter incoming methods with. + */ + public BlockToAccessFrameListener(int channelId) + { + super(channelId); + _logger.info("Creating a listener:" + this); + } + + public boolean processMethod(int channelId, AMQMethodBody frame) + { + return true; + } + + @Override + public void error(Exception e) + { + _logger.info("Exception(" + e + ") Received by:" + this); + // Create a new Thread to start the blocking registration. + new Thread(new Runnable() + { + + public void run() + { + //Set an initial listener that will allow us to create a new blocking method + try + { + _handler.writeCommandFrameAndWaitForReply(_blockFrame, null, 2000L); + _logger.info("listener(" + this + ") Wait completed"); + } + catch (Exception e) + { + _logger.info("listener(" + this + ") threw exception:" + e.getMessage()); + _receivedException = e; + } + + _logger.info("listener(" + this + ") completed"); + _handleCountDown.countDown(); + } + }).start(); + } + + public Exception getReceivedException() + { + return _receivedException; + } + } + +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java new file mode 100644 index 0000000000..f0938a4bc0 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/client/protocol/MockIoSession.java @@ -0,0 +1,312 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client.protocol; + +import org.apache.mina.common.*; +import org.apache.mina.common.support.DefaultCloseFuture; +import org.apache.mina.common.support.DefaultWriteFuture; +import org.apache.mina.common.support.AbstractIoFilterChain; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.util.Set; + +public class MockIoSession implements IoSession +{ + private AMQProtocolSession _protocolSession; + + /** + * Stores the last response written + */ + private Object _lastWrittenObject; + + private boolean _closing; + private IoFilterChain _filterChain; + + public MockIoSession() + { + _filterChain = new AbstractIoFilterChain(this) + { + protected void doWrite(IoSession ioSession, IoFilter.WriteRequest writeRequest) throws Exception + { + + } + + protected void doClose(IoSession ioSession) throws Exception + { + + } + }; + } + + public Object getLastWrittenObject() + { + return _lastWrittenObject; + } + + public IoService getService() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoServiceConfig getServiceConfig() + { + return null; + } + + public IoHandler getHandler() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoSessionConfig getConfig() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public IoFilterChain getFilterChain() + { + return _filterChain; + } + + public WriteFuture write(Object message) + { + WriteFuture wf = new DefaultWriteFuture(null); + _lastWrittenObject = message; + return wf; + } + + public CloseFuture close() + { + _closing = true; + CloseFuture cf = new DefaultCloseFuture(null); + cf.setClosed(); + return cf; + } + + public Object getAttachment() + { + return _protocolSession; + } + + public Object setAttachment(Object attachment) + { + Object current = _protocolSession; + _protocolSession = (AMQProtocolSession) attachment; + return current; + } + + public Object getAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object setAttribute(String key, Object value) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object setAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public Object removeAttribute(String key) + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean containsAttribute(String key) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public Set getAttributeKeys() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public TransportType getTransportType() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isConnected() + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isClosing() + { + return _closing; + } + + public CloseFuture getCloseFuture() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getRemoteAddress() + { + return new InetSocketAddress("127.0.0.1", 1234); //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getLocalAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public SocketAddress getServiceAddress() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getIdleTime(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public int getWriteTimeout() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getWriteTimeoutInMillis() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setWriteTimeout(int writeTimeout) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public TrafficMask getTrafficMask() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public void setTrafficMask(TrafficMask trafficMask) + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void suspendRead() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void suspendWrite() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void resumeRead() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void resumeWrite() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public long getReadBytes() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getWrittenBytes() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getReadMessages() + { + return 0L; + } + + public long getWrittenMessages() + { + return 0L; + } + + public long getWrittenWriteRequests() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteRequests() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getScheduledWriteBytes() + { + return 0; //TODO + } + + public long getCreationTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastIoTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastReadTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastWriteTime() + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isIdle(IdleStatus status) + { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + public int getIdleCount(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java new file mode 100644 index 0000000000..ddbc69826d --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java @@ -0,0 +1,96 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.basic; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javax.jms.JMSException; + +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.TestMessageHelper; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; + +public class FieldTableKeyEnumeratorTest extends TestCase +{ + public void testTrue() + { + + } + public void testKeyEnumeration() + { + FieldTable result = FieldTableFactory.newFieldTable(); + result.setObject("one", 1L); + result.setObject("two", 2L); + result.setObject("three", 3L); + result.setObject("four", 4L); + result.setObject("five", 5L); + + Iterator iterator = result.keys().iterator(); + + try + { + assertTrue("one".equals(iterator.next())); + assertTrue("two".equals(iterator.next())); + assertTrue("three".equals(iterator.next())); + assertTrue("four".equals(iterator.next())); + assertTrue("five".equals(iterator.next())); + } + catch (NoSuchElementException e) + { + fail("All elements should be found."); + } + + } + + public void testPropertEnu() + { + try + { + JMSTextMessage text = TestMessageHelper.newJMSTextMessage(); + + text.setBooleanProperty("Boolean1", true); + text.setBooleanProperty("Boolean2", true); + text.setIntProperty("Int", 2); + text.setLongProperty("Long", 2); + + Enumeration e = text.getPropertyNames(); + + assertTrue("Boolean1".equals(e.nextElement())); + assertTrue("Boolean2".equals(e.nextElement())); + assertTrue("Int".equals(e.nextElement())); + assertTrue("Long".equals(e.nextElement())); + } + catch (JMSException e) + { + + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(FieldTableKeyEnumeratorTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java new file mode 100644 index 0000000000..60ed688897 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.basic; + +import java.util.Enumeration; + +import javax.jms.JMSException; + +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.TestMessageHelper; + +public class FieldTablePropertyTest extends TestCase +{ + public void testPropertyNames() + { + try + { + JMSTextMessage text = TestMessageHelper.newJMSTextMessage(); + + text.setBooleanProperty("Boolean1", true); + text.setBooleanProperty("Boolean2", true); + text.setIntProperty("Int", 2); + text.setLongProperty("Long", 2); + + Enumeration e = text.getPropertyNames(); + + assertEquals("Boolean1", e.nextElement()); + assertTrue("Boolean2".equals(e.nextElement())); + assertTrue("Int".equals(e.nextElement())); + assertTrue("Long".equals(e.nextElement())); + } + catch (JMSException e) + { + + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(FieldTablePropertyTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java new file mode 100644 index 0000000000..1b27ff6300 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java @@ -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. + * + * + */ +package org.apache.qpid.test.unit.client.BrokerDetails; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.url.URLSyntaxException; + +public class BrokerDetailsTest extends TestCase +{ + public void testMultiParameters() throws URLSyntaxException + { + String url = "tcp://localhost:5672?timeout='200',immediatedelivery='true'"; + + AMQBrokerDetails broker = new AMQBrokerDetails(url); + + assertTrue(broker.getProperty("timeout").equals("200")); + assertTrue(broker.getProperty("immediatedelivery").equals("true")); + } + + public void testVMBroker() throws URLSyntaxException + { + String url = "vm://:2"; + + AMQBrokerDetails broker = new AMQBrokerDetails(url); + assertTrue(broker.getTransport().equals("vm")); + assertEquals(broker.getPort(), 2); + } + + public void testTransportsDefaultToTCP() throws URLSyntaxException + { + String url = "localhost:5672"; + + AMQBrokerDetails broker = new AMQBrokerDetails(url); + assertTrue(broker.getTransport().equals("tcp")); + } + + public void testCheckDefaultPort() throws URLSyntaxException + { + String url = "tcp://localhost"; + + AMQBrokerDetails broker = new AMQBrokerDetails(url); + assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT); + } + + public void testBothDefaults() throws URLSyntaxException + { + String url = "localhost"; + + AMQBrokerDetails broker = new AMQBrokerDetails(url); + + assertTrue(broker.getTransport().equals("tcp")); + assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT); + } + + public void testWrongOptionSeparatorInBroker() + { + String url = "tcp://localhost:5672+option='value'"; + try + { + new AMQBrokerDetails(url); + } + catch (URLSyntaxException urise) + { + assertTrue(urise.getReason().equals("Illegal character in port number")); + } + + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(BrokerDetailsTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java new file mode 100644 index 0000000000..66f220643c --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.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.test.unit.client.channelclose; + +import org.apache.qpid.AMQChannelClosedException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidArgumentException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.protocol.AMQConstant; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelCloseMethodHandlerNoCloseOk implements StateAwareMethodListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandlerNoCloseOk.class); + + private static ChannelCloseMethodHandlerNoCloseOk _handler = new ChannelCloseMethodHandlerNoCloseOk(); + + public static ChannelCloseMethodHandlerNoCloseOk getInstance() + { + return _handler; + } + + public void methodReceived(AMQProtocolSession session, ChannelCloseBody method, int channelId) + throws AMQException + { + _logger.debug("ChannelClose method received"); + + AMQConstant errorCode = AMQConstant.getConstant(method.getReplyCode()); + AMQShortString reason = method.getReplyText(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); + } + + // For this test Method Handler .. don't send Close-OK + // // TODO: Be aware of possible changes to parameter order as versions change. + // AMQFrame frame = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), method.getMajor(), method.getMinor()); + // protocolSession.writeFrame(frame); + if (errorCode != AMQConstant.REPLY_SUCCESS) + { + _logger.error("Channel close received with errorCode " + errorCode + ", and reason " + reason); + if (errorCode == AMQConstant.NO_CONSUMERS) + { + throw new AMQNoConsumersException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new AMQInvalidArgumentException(String.valueOf(reason), null); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason), null); + } + else + { + throw new AMQChannelClosedException(errorCode, "Error: " + reason, null); + } + + } + + session.channelClosed(channelId, errorCode, String.valueOf(reason)); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java new file mode 100644 index 0000000000..c7eb745566 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.test.unit.client.channelclose; + +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.handler.ConnectionStartMethodHandler; +import org.apache.qpid.client.handler.ConnectionCloseMethodHandler; +import org.apache.qpid.client.handler.ConnectionTuneMethodHandler; +import org.apache.qpid.client.handler.ConnectionSecureMethodHandler; +import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler; +import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler; +import org.apache.qpid.client.handler.BasicDeliverMethodHandler; +import org.apache.qpid.client.handler.BasicReturnMethodHandler; +import org.apache.qpid.client.handler.BasicCancelOkMethodHandler; +import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler; +import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler; +import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.framing.ConnectionStartBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionTuneBody; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionOpenOkBody; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.BasicReturnBody; +import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.framing.ChannelFlowOkBody; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.framing.ExchangeBoundOkBody; + +import java.util.Map; +import java.util.HashMap; + +public class NoCloseOKStateManager extends AMQStateManager +{ + public NoCloseOKStateManager(AMQProtocolSession protocolSession) + { + super(protocolSession); + } + + + + +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java new file mode 100644 index 0000000000..d05e90823c --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java @@ -0,0 +1,532 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.connectionurl; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQBrokerDetails; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.url.URLSyntaxException; + +public class ConnectionURLTest extends TestCase +{ + + public void testFailoverURL() throws URLSyntaxException + { + String url = "amqp://ritchiem:bob@/test?brokerlist='tcp://localhost:5672;tcp://fancyserver:3000/',failover='roundrobin?cyclecount='100''"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod().equals("roundrobin")); + assertEquals("100", connectionurl.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE)); + assertTrue(connectionurl.getUsername().equals("ritchiem")); + assertTrue(connectionurl.getPassword().equals("bob")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 2); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + + service = connectionurl.getBrokerDetails(1); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("fancyserver")); + assertTrue(service.getPort() == 3000); + + } + + public void testSingleTransportUsernamePasswordURL() throws URLSyntaxException + { + String url = "amqp://ritchiem:bob@/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("ritchiem")); + assertTrue(connectionurl.getPassword().equals("bob")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + } + + public void testSingleTransportUsernameBlankPasswordURL() throws URLSyntaxException + { + String url = "amqp://ritchiem:@/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("ritchiem")); + assertTrue(connectionurl.getPassword().equals("")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + } + + public void testFailedURLNullPassword() + { + String url = "amqp://ritchiem@/test?brokerlist='tcp://localhost:5672'"; + + try + { + new AMQConnectionURL(url); + fail("URL has null password"); + } + catch (URLSyntaxException e) + { + assertTrue(e.getReason().equals("Null password in user information not allowed.")); + assertTrue(e.getIndex() == 7); + } + } + + + public void testSingleTransportURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + } + + public void testSingleTransportWithClientURLURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@clientname/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + assertTrue(connectionurl.getClientName().equals("clientname")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + } + + public void testSingleTransport1OptionURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672',routingkey='jim'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + assertTrue(connectionurl.getOption("routingkey").equals("jim")); + } + + public void testSingleTransportDefaultedBroker() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='localhost'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + } + + public void testSingleTransportDefaultedBrokerWithPort() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='localhost:1234'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 1234); + } + + public void testSingleTransportDefaultedBrokerWithIP() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='127.0.0.1'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + assertTrue(service.getHost().equals("127.0.0.1")); + assertTrue(service.getPort() == 5672); + } + + public void testSingleTransportDefaultedBrokerWithIPandPort() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='127.0.0.1:1234'"; + +// ConnectionURL connectionurl = new AMQConnectionURL(url); +// +// assertTrue(connectionurl.getFailoverMethod() == null); +// assertTrue(connectionurl.getUsername().equals("guest")); +// assertTrue(connectionurl.getPassword().equals("guest")); +// assertTrue(connectionurl.getVirtualHost().equals("/temp")); +// +// +// assertTrue(connectionurl.getBrokerCount() == 1); +// +// BrokerDetails service = connectionurl.getBrokerDetails(0); +// +// assertTrue(service.getTransport().equals("tcp")); +// +// assertTrue(service.getHost().equals("127.0.0.1")); +// assertTrue(service.getPort() == 1234); + } + + + public void testSingleTransportMultiOptionURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672?foo='jim'&bar='bob'&fred='jimmy'',routingkey='jim',timeout='200',immediatedelivery='true'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + + assertTrue(connectionurl.getOption("routingkey").equals("jim")); + assertTrue(connectionurl.getOption("timeout").equals("200")); + assertTrue(connectionurl.getOption("immediatedelivery").equals("true")); + } + + public void testSinglevmURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='vm://:2'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("vm")); + assertTrue(service.getHost().equals("")); + assertTrue(service.getPort() == 2); + + } + + public void testFailoverVMURL() throws URLSyntaxException + { + String url = "amqp://ritchiem:bob@/test?brokerlist='vm://:2;vm://:3',failover='roundrobin'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod().equals("roundrobin")); + assertTrue(connectionurl.getUsername().equals("ritchiem")); + assertTrue(connectionurl.getPassword().equals("bob")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 2); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("vm")); + assertTrue(service.getHost().equals("")); + assertTrue(service.getPort() == 2); + + service = connectionurl.getBrokerDetails(1); + assertTrue(service.getTransport().equals("vm")); + assertTrue(service.getHost().equals("")); + assertTrue(service.getPort() == 3); + } + + + public void testNoVirtualHostURL() + { + String url = "amqp://user@?brokerlist='tcp://localhost:5672'"; + + try + { + new AMQConnectionURL(url); + fail("URL has no virtual host should not parse"); + } + catch (URLSyntaxException e) + { + // This should occur. + } + } + + public void testNoClientID() throws URLSyntaxException + { + String url = "amqp://user:@/test?brokerlist='tcp://localhost:5672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getUsername().equals("user")); + assertTrue(connectionurl.getPassword().equals("")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + } + + + public void testWrongOptionSeparatorInOptions() + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'+failover='roundrobin'"; + try + { + new AMQConnectionURL(url); + fail("URL Should not parse"); + } + catch (URLSyntaxException urise) + { + assertTrue(urise.getReason().equals("Unterminated option. Possible illegal option separator:'+'")); + } + + } + + + public void testNoUserDetailsProvidedWithClientID() + + { + String url = "amqp://clientID/test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'"; + try + { + new AMQConnectionURL(url); + fail("URL Should not parse"); + } + catch (URLSyntaxException urise) + { + assertTrue(urise.getMessage().startsWith("User information not found on url")); + } + + } + + public void testNoUserDetailsProvidedNOClientID() + + { + String url = "amqp:///test?brokerlist='tcp://localhost:5672;tcp://localhost:5673'"; + try + { + new AMQConnectionURL(url); + fail("URL Should not parse"); + } + catch (URLSyntaxException urise) + { + assertTrue(urise.getMessage().startsWith("User information not found on url")); + } + + } + + public void testCheckVirtualhostFormat() throws URLSyntaxException + { + String url = "amqp://guest:guest@/t.-_+!=:?brokerlist='tcp://localhost:5672'"; + + AMQConnectionURL connection = new AMQConnectionURL(url); + assertTrue(connection.getVirtualHost().equals("/t.-_+!=:")); + } + + public void testCheckDefaultPort() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test=:?brokerlist='tcp://localhost'"; + + AMQConnectionURL connection = new AMQConnectionURL(url); + + BrokerDetails broker = connection.getBrokerDetails(0); + assertTrue(broker.getPort() == AMQBrokerDetails.DEFAULT_PORT); + + } + + public void testCheckMissingFinalQuote() throws URLSyntaxException + { + String url = "amqp://guest:guest@id/test" + "?brokerlist='tcp://localhost:5672"; + + try + { + new AMQConnectionURL(url); + } + catch (URLSyntaxException e) + { + assertEquals(e.getMessage(), "Unterminated option at index 32: brokerlist='tcp://localhost:5672"); + } + } + + + public void testDefaultExchanges() throws URLSyntaxException + { + String url = "amqp://guest:guest@id/test" + "?defaultQueueExchange='test.direct'&defaultTopicExchange='test.topic'&temporaryQueueExchange='tmp.direct'&temporaryTopicExchange='tmp.topic'"; + + AMQConnectionURL conn = new AMQConnectionURL(url); + + assertEquals(conn.getDefaultQueueExchangeName(),"test.direct"); + + assertEquals(conn.getDefaultTopicExchangeName(),"test.topic"); + + assertEquals(conn.getTemporaryQueueExchangeName(),"tmp.direct"); + + assertEquals(conn.getTemporaryTopicExchangeName(),"tmp.topic"); + + } + + public void testSocketProtocol() throws URLSyntaxException + { + String url = "amqp://guest:guest@id/test" + "?brokerlist='socket://VM-Unique-socketID'"; + + try + { + AMQConnectionURL curl = new AMQConnectionURL(url); + assertNotNull(curl); + assertEquals(1, curl.getBrokerCount()); + assertNotNull(curl.getBrokerDetails(0)); + assertEquals(BrokerDetails.SOCKET, curl.getBrokerDetails(0).getTransport()); + assertEquals("VM-Unique-socketID", curl.getBrokerDetails(0).getHost()); + assertEquals("URL does not toString as expected", + url.replace(":guest", ":********"), curl.toString()); + } + catch (URLSyntaxException e) + { + fail(e.getMessage()); + } + } + + public void testSingleTransportMultiOptionOnBrokerURL() throws URLSyntaxException + { + String url = "amqp://guest:guest@/test?brokerlist='tcp://localhost:5672?foo='jim'&bar='bob'&fred='jimmy'',routingkey='jim',timeout='200',immediatedelivery='true'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getFailoverMethod() == null); + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + + BrokerDetails service = connectionurl.getBrokerDetails(0); + + assertTrue(service.getTransport().equals("tcp")); + + + assertTrue(service.getHost().equals("localhost")); + assertTrue(service.getPort() == 5672); + assertEquals("jim",service.getProperty("foo")); + assertEquals("bob",service.getProperty("bar")); + assertEquals("jimmy",service.getProperty("fred")); + + assertTrue(connectionurl.getOption("routingkey").equals("jim")); + assertTrue(connectionurl.getOption("timeout").equals("200")); + assertTrue(connectionurl.getOption("immediatedelivery").equals("true")); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ConnectionURLTest.class); + } +} + diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java new file mode 100644 index 0000000000..2a66b86985 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java @@ -0,0 +1,185 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.destinationurl; + +import junit.framework.TestCase; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.url.AMQBindingURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URISyntaxException; + +public class DestinationURLTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(DestinationURLTest.class); + + public void testFullURL() throws URISyntaxException + { + + String url = "exchange.Class://exchangeName/Destination/Queue"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(url.equals(dest.toString())); + + assertTrue(dest.getExchangeClass().equals("exchange.Class")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("Destination")); + assertTrue(dest.getQueueName().equals("Queue")); + } + + public void testQueue() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName//Queue"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(url.equals(dest.toString())); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("")); + assertTrue(dest.getQueueName().equals("Queue")); + } + + public void testQueueWithOption() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName//Queue?option='value'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(url.equals(dest.toString())); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("")); + assertTrue(dest.getQueueName().equals("Queue")); + assertTrue(dest.getOption("option").equals("value")); + } + + + public void testDestination() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName/Destination/"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(url.equals(dest.toString())); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("Destination")); + assertTrue(dest.getQueueName().equals("")); + } + + public void testDestinationWithOption() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName/Destination/?option='value'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(url.equals(dest.toString())); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("Destination")); + assertTrue(dest.getQueueName().equals("")); + + assertTrue(dest.getOption("option").equals("value")); + } + + public void testDestinationWithMultiOption() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName/Destination/?option='value',option2='value2'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("Destination")); + assertTrue(dest.getQueueName().equals("")); + + assertTrue(dest.getOption("option").equals("value")); + assertTrue(dest.getOption("option2").equals("value2")); + } + + public void testDestinationWithNoExchangeDefaultsToDirect() throws URISyntaxException + { + + String url = "IBMPerfQueue1?durable='true'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(dest.getExchangeClass().equals(ExchangeDefaults.DIRECT_EXCHANGE_CLASS)); + assertTrue(dest.getExchangeName().equals("")); + assertTrue(dest.getDestinationName().equals("")); + assertTrue(dest.getQueueName().equals("IBMPerfQueue1")); + + assertTrue(dest.getOption("durable").equals("true")); + } + + public void testDestinationWithMultiBindingKeys() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName/Destination/?bindingkey='key1',bindingkey='key2'"; + + AMQBindingURL dest = new AMQBindingURL(url); + + assertTrue(dest.getExchangeClass().equals("exchangeClass")); + assertTrue(dest.getExchangeName().equals("exchangeName")); + assertTrue(dest.getDestinationName().equals("Destination")); + assertTrue(dest.getQueueName().equals("")); + + assertTrue(dest.getBindingKeys().length == 2); + } + + // You can only specify only a routing key or binding key, but not both. + public void testDestinationIfOnlyRoutingKeyOrBindingKeyIsSpecified() throws URISyntaxException + { + + String url = "exchangeClass://exchangeName/Destination/?bindingkey='key1',routingkey='key2'"; + boolean exceptionThrown = false; + try + { + + AMQBindingURL dest = new AMQBindingURL(url); + } + catch(URISyntaxException e) + { + exceptionThrown = true; + _logger.info("Exception thrown",e); + } + + assertTrue("Failed to throw an URISyntaxException when both the bindingkey and routingkey is specified",exceptionThrown); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(DestinationURLTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java new file mode 100644 index 0000000000..bbabf0b57d --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java @@ -0,0 +1,569 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.message; + +import java.util.HashMap; + +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSBytesMessage; +import org.apache.qpid.client.message.TestMessageHelper; + +public class BytesMessageTest extends TestCase +{ + /** + * Tests that on creation a call to getBodyLength() throws an exception + * if null was passed in during creation + */ + public void testNotReadableOnCreationWithNull() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.getBodyLength(); + fail("expected exception did not occur"); + } + catch (MessageNotReadableException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageNotReadableException, got " + e); + } + } + + public void testResetMakesReadble() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeInt(10); + bm.reset(); + bm.writeInt(12); + fail("expected exception did not occur"); + } + catch (MessageNotWriteableException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageNotWriteableException, got " + e); + } + } + + public void testClearBodyMakesWritable() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeInt(10); + bm.reset(); + bm.clearBody(); + bm.writeInt(10); + } + + public void testWriteBoolean() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeBoolean(true); + bm.writeBoolean(false); + bm.reset(); + boolean val = bm.readBoolean(); + assertEquals(true, val); + val = bm.readBoolean(); + assertEquals(false, val); + } + + public void testWriteInt() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeInt(10); + bm.reset(); + long len = bm.getBodyLength(); + assertTrue(len == 4); + int val = bm.readInt(); + assertTrue(val == 10); + } + + public void testWriteString() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeUTF("Bananas"); + bm.reset(); + String res = bm.readUTF(); + assertEquals("Bananas", res); + } + + public void testWriteBytes() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + byte[] bytes = {1,2,3,4}; + bm.writeBytes(bytes, 1, 2); + bm.reset(); + bytes = new byte[2]; + bm.readBytes(bytes); + assertEquals(2, bytes[0]); + assertEquals(3, bytes[1]); + } + + public void testWriteObject() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeObject(new Boolean(true)); + bm.writeObject(new Boolean(false)); + bm.writeObject(new Byte((byte)2)); + bm.writeObject(new byte[]{1,2,3,4}); + bm.writeObject(new Character('g')); + bm.writeObject(new Short((short) 29)); + bm.writeObject(new Integer(101)); + bm.writeObject(new Long(50003222L)); + bm.writeObject("Foobar"); + bm.writeObject(new Float(1.7f)); + bm.writeObject(new Double(8.7d)); + bm.reset(); + assertTrue(bm.readBoolean()); + assertTrue(!bm.readBoolean()); + assertEquals((byte)2, bm.readByte()); + byte[] bytes = new byte[4]; + bm.readBytes(bytes); + assertEquals('g', bm.readChar()); + assertEquals((short) 29, bm.readShort()); + assertEquals(101, bm.readInt()); + assertEquals(50003222L, bm.readLong()); + assertEquals("Foobar", bm.readUTF()); + assertEquals(1.7f, bm.readFloat()); + assertEquals(8.7d, bm.readDouble()); + } + + public void testWriteObjectRejectsNonPrimitives() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeObject(new HashMap()); + fail("expected MessageFormatException was not thrown"); + } + catch (MessageFormatException e) + { + // pass + } + } + + public void testWriteObjectThrowsNPE() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeObject(null); + fail("expected exception did not occur"); + } + catch (NullPointerException n) + { + // ok + } + catch (Exception e) + { + fail("expected NullPointerException, got " + e); + } + } + + public void testReadBoolean() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeBoolean(true); + bm.reset(); + boolean result = bm.readBoolean(); + assertTrue(result); + } + + public void testReadUnsignedByte() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte) 9); + bm.reset(); + int result = bm.readUnsignedByte(); + assertEquals(9, result); + } + + public void testReadUnsignedShort() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeShort((byte) 9); + bm.reset(); + int result = bm.readUnsignedShort(); + assertEquals(9, result); + } + + public void testReadBytesChecksNull() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.readBytes(null); + } + catch (IllegalArgumentException e) + { + // pass + } + + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.readBytes(null, 1); + } + catch (IllegalArgumentException e) + { + // pass + } + } + + public void testReadBytesChecksMaxSize() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + byte[] bytes = new byte[100]; + bm.readBytes(bytes, 120); + } + catch (IllegalArgumentException e) + { + // pass + } + } + + public void testReadBytesReturnsCorrectLengths() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + byte[] bytes = {2, 3}; + bm.writeBytes(bytes); + bm.reset(); + int len = bm.readBytes(bytes); + assertEquals(2, len); + len = bm.readBytes(bytes); + assertEquals(-1, len); + len = bm.readBytes(bytes, 2); + assertEquals(-1, len); + bm.reset(); + len = bm.readBytes(bytes, 2); + assertEquals(2, len); + bm.reset(); + len = bm.readBytes(bytes, 1); + assertEquals(1, len); + + } + + public void testEOFByte() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)1); + bm.reset(); + bm.readByte(); + // should throw + bm.readByte(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFUnsignedByte() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)1); + bm.reset(); + bm.readByte(); + // should throw + bm.readUnsignedByte(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFBoolean() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeBoolean(true); + bm.reset(); + bm.readBoolean(); + // should throw + bm.readBoolean(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFChar() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeChar('A'); + bm.reset(); + bm.readChar(); + // should throw + bm.readChar(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFDouble() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeDouble(1.3d); + bm.reset(); + bm.readDouble(); + // should throw + bm.readDouble(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFFloat() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeFloat(1.3f); + bm.reset(); + bm.readFloat(); + // should throw + bm.readFloat(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFInt() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeInt(99); + bm.reset(); + bm.readInt(); + // should throw + bm.readInt(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFLong() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeLong(4L); + bm.reset(); + bm.readLong(); + // should throw + bm.readLong(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFShort() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeShort((short)4); + bm.reset(); + bm.readShort(); + // should throw + bm.readShort(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFUnsignedShort() throws Exception + { + try + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeShort((short)4); + bm.reset(); + bm.readUnsignedShort(); + // should throw + bm.readUnsignedShort(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + /** + * Tests that the readBytes() method populates the passed in array + * correctly + * @throws Exception + */ + public void testReadBytes() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)3); + bm.writeByte((byte)4); + bm.reset(); + byte[] result = new byte[2]; + int count = bm.readBytes(result); + assertEquals((byte)3, result[0]); + assertEquals((byte)4, result[1]); + assertEquals(2, count); + } + + public void testReadBytesEOF() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)3); + bm.writeByte((byte)4); + bm.reset(); + byte[] result = new byte[2]; + bm.readBytes(result); + int count = bm.readBytes(result); + assertEquals(-1, count); + } + + public void testReadBytesWithLargerArray() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)3); + bm.writeByte((byte)4); + bm.reset(); + byte[] result = new byte[3]; + int count = bm.readBytes(result); + assertEquals(2, count); + assertEquals((byte)3, result[0]); + assertEquals((byte)4, result[1]); + assertEquals((byte)0, result[2]); + } + + public void testReadBytesWithCount() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.writeByte((byte)3); + bm.writeByte((byte)4); + bm.writeByte((byte)5); + bm.reset(); + byte[] result = new byte[3]; + int count = bm.readBytes(result, 2); + assertEquals(2, count); + assertEquals((byte)3, result[0]); + assertEquals((byte)4, result[1]); + assertEquals((byte)0, result[2]); + } + + public void testToBodyStringWithNull() throws Exception + { + JMSBytesMessage bm = TestMessageHelper.newJMSBytesMessage(); + bm.reset(); + String result = bm.toBodyString(); + assertNull(result); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(BytesMessageTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java new file mode 100644 index 0000000000..3e04c36b38 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java @@ -0,0 +1,383 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.test.unit.client.message; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.message.TestMessageHelper; + + +public class MapMessageTest extends TestCase +{ + + //Test Lookups + + public void testBooleanLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + + mm.setBoolean("value", true); + Assert.assertEquals(true, mm.getBoolean("value")); + Assert.assertEquals("true", mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testByteLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setByte("value", Byte.MAX_VALUE); + + Assert.assertEquals(Byte.MAX_VALUE, mm.getByte("value")); + Assert.assertEquals((short) Byte.MAX_VALUE, mm.getShort("value")); + Assert.assertEquals(Byte.MAX_VALUE, mm.getInt("value")); + Assert.assertEquals((long) Byte.MAX_VALUE, mm.getLong("value")); + Assert.assertEquals("" + Byte.MAX_VALUE, mm.getString("value")); + + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testShortLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setShort("value", Short.MAX_VALUE); + Assert.assertEquals(Short.MAX_VALUE, mm.getShort("value")); + Assert.assertEquals((int) Short.MAX_VALUE, mm.getInt("value")); + Assert.assertEquals((long) Short.MAX_VALUE, mm.getLong("value")); + Assert.assertEquals("" + Short.MAX_VALUE, mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + + public void testCharLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + + mm.setChar("value", 'c'); + Assert.assertEquals('c', mm.getChar("value")); + Assert.assertEquals("c", mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + + mm.setString("value", null); + mm.getChar("value"); + fail("Expected NullPointerException"); + + } + catch (NullPointerException e) + { + ; // pass + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + + + + } + + public void testDoubleLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setDouble("value", Double.MAX_VALUE); + Assert.assertEquals(Double.MAX_VALUE, mm.getDouble("value")); + Assert.assertEquals("" + Double.MAX_VALUE, mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFloatLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setFloat("value", Float.MAX_VALUE); + Assert.assertEquals(Float.MAX_VALUE, mm.getFloat("value")); + Assert.assertEquals((double) Float.MAX_VALUE, mm.getDouble("value")); + Assert.assertEquals("" + Float.MAX_VALUE, mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testIntLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setInt("value", Integer.MAX_VALUE); + Assert.assertEquals(Integer.MAX_VALUE, mm.getInt("value")); + Assert.assertEquals((long) Integer.MAX_VALUE, mm.getLong("value")); + Assert.assertEquals("" + Integer.MAX_VALUE, mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testLongLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setLong("value", Long.MAX_VALUE); + Assert.assertEquals(Long.MAX_VALUE, mm.getLong("value")); + Assert.assertEquals("" + Long.MAX_VALUE, mm.getString("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testBytesLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + byte[] bytes = {99, 98, 97, 96, 95}; + mm.setBytes("bytes", bytes); + assertBytesEqual(bytes, mm.getBytes("bytes")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + // Failed Lookups + + public void testFailedBooleanLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + Assert.assertEquals(false, mm.getBoolean("int")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFailedByteLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getByte("random"); + Assert.fail("NumberFormatException expected"); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + + } + + public void testFailedBytesLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getBytes("random"); + Assert.fail("MessageFormatException expected"); + } + catch (MessageFormatException mfe) + { + //normal path + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedCharLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getChar("random"); + Assert.fail("MessageFormatException expected"); + } + catch (MessageFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedDoubleLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getDouble("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedFloatLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getFloat("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedIntLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getInt("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedLongLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getLong("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedShortLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getShort("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + + private void assertBytesEqual(byte[] expected, byte[] actual) + { + Assert.assertEquals(expected.length, actual.length); + + for (int index = 0; index < expected.length; index++) + { + Assert.assertEquals(expected[index], actual[index]); + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MapMessageTest.class); + } + + +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java new file mode 100644 index 0000000000..802f1e6c2e --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java @@ -0,0 +1,623 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.message; + +import java.util.HashMap; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; +import javax.jms.StreamMessage; + +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSStreamMessage; +import org.apache.qpid.client.message.TestMessageHelper; + +/** + * @author Apache Software Foundation + */ +public class StreamMessageTest extends TestCase +{ + /** + * Tests that on creation a call to getBodyLength() throws an exception + * if null was passed in during creation + */ + public void testNotReadableOnCreationWithNull() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.readByte(); + fail("expected exception did not occur"); + } + catch (MessageNotReadableException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageNotReadableException, got " + e); + } + } + + public void testResetMakesReadble() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeInt(10); + bm.reset(); + bm.writeInt(12); + fail("expected exception did not occur"); + } + catch (MessageNotWriteableException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageNotWriteableException, got " + e); + } + } + + public void testClearBodyMakesWritable() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeInt(10); + bm.reset(); + bm.clearBody(); + bm.writeInt(10); + } + + public void testWriteBoolean() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeBoolean(true); + bm.writeBoolean(false); + bm.reset(); + boolean val = bm.readBoolean(); + assertEquals(true, val); + val = bm.readBoolean(); + assertEquals(false, val); + } + + public void testWriteInt() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeInt(10); + bm.reset(); + int val = bm.readInt(); + assertTrue(val == 10); + } + + public void testWriteString() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeString("Bananas"); + bm.reset(); + String res = bm.readString(); + assertEquals("Bananas", res); + } + + public void testWriteBytes() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + byte[] bytes = {1,2,3,4}; + bm.writeBytes(bytes, 1, 2); + bm.reset(); + bytes = new byte[2]; + bm.readBytes(bytes); + assertEquals(2, bytes[0]); + assertEquals(3, bytes[1]); + } + + public void testWriteObject() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeObject(new Boolean(true)); + bm.writeObject(new Boolean(false)); + bm.writeObject(new Byte((byte)2)); + bm.writeObject(new byte[]{1,2,3,4}); + bm.writeObject(new Character('g')); + bm.writeObject(new Short((short) 29)); + bm.writeObject(new Integer(101)); + bm.writeObject(new Long(50003222L)); + bm.writeObject("Foobar"); + bm.writeObject(new Float(1.7f)); + bm.writeObject(new Double(8.7d)); + bm.reset(); + assertTrue(bm.readBoolean()); + assertTrue(!bm.readBoolean()); + assertEquals((byte)2, bm.readByte()); + byte[] bytes = new byte[4]; + bm.readBytes(bytes); + assertEquals('g', bm.readChar()); + assertEquals((short) 29, bm.readShort()); + assertEquals(101, bm.readInt()); + assertEquals(50003222L, bm.readLong()); + assertEquals("Foobar", bm.readString()); + assertEquals(1.7f, bm.readFloat()); + assertEquals(8.7d, bm.readDouble()); + } + + public void testWriteObjectRejectsNonPrimitives() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeObject(new HashMap()); + fail("expected MessageFormatException was not thrown"); + } + catch (MessageFormatException e) + { + // pass + } + } + + public void testReadBoolean() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeBoolean(true); + bm.reset(); + boolean result = bm.readBoolean(); + assertTrue(result); + } + + public void testReadBytesChecksNull() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.readBytes(null); + } + catch (IllegalArgumentException e) + { + // pass + } + } + + public void testReadBytesReturnsCorrectLengths() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + byte[] bytes = {2, 3}; + bm.writeBytes(bytes); + bm.writeBytes(null); + bm.writeBytes(new byte[]{}); + bm.reset(); + int len = bm.readBytes(bytes); + assertEquals(2, len); + len = bm.readBytes(bytes); + assertEquals(-1, len); + len = bm.readBytes(bytes); + assertEquals(-1, len); + len = bm.readBytes(bytes); + assertEquals(0, len); + } + + public void testReadBytesFollowedByPrimitive() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeBytes(new byte[]{2, 3, 4, 5, 6, 7, 8}); + bm.writeBytes(new byte[]{2, 3, 4, 5, 6, 7}); + bm.writeString("Foo"); + bm.reset(); + int len; + do + { + len = bm.readBytes(new byte[2]); + } + while (len == 2); + + do + { + len = bm.readBytes(new byte[2]); + } + while (len == 2); + + assertEquals("Foo", bm.readString()); + } + + public void testReadMultipleByteArrays() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + byte[] bytes = {2, 3, 4}; + bm.writeBytes(bytes); + bm.writeBytes(bytes); + bm.reset(); + byte[] result = new byte[2]; + int len = bm.readBytes(result); + assertEquals(2, len); + len = bm.readBytes(result); + assertEquals(1, len); + len = bm.readBytes(result); + assertEquals(2, len); + } + + public void testEOFByte() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeByte((byte)1); + bm.reset(); + bm.readByte(); + // should throw + bm.readByte(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFBoolean() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeBoolean(true); + bm.reset(); + bm.readBoolean(); + // should throw + bm.readBoolean(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFChar() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeChar('A'); + bm.reset(); + bm.readChar(); + // should throw + bm.readChar(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFDouble() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeDouble(1.3d); + bm.reset(); + bm.readDouble(); + // should throw + bm.readDouble(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFFloat() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeFloat(1.3f); + bm.reset(); + bm.readFloat(); + // should throw + bm.readFloat(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFInt() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeInt(99); + bm.reset(); + bm.readInt(); + // should throw + bm.readInt(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFLong() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeLong(4L); + bm.reset(); + bm.readLong(); + // should throw + bm.readLong(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testEOFShort() throws Exception + { + try + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeShort((short)4); + bm.reset(); + bm.readShort(); + // should throw + bm.readShort(); + fail("expected exception did not occur"); + } + catch (MessageEOFException m) + { + // ok + } + catch (Exception e) + { + fail("expected MessageEOFException, got " + e); + } + } + + public void testToBodyStringWithNull() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.reset(); + String result = bm.toBodyString(); + assertNull(result); + } + + private void checkConversionsFail(StreamMessage sm, int[] conversions) throws JMSException + { + for (int conversion : conversions) + { + try + { + switch (conversion) + { + case 0: + sm.readBoolean(); + break; + case 1: + sm.readByte(); + break; + case 2: + sm.readShort(); + break; + case 3: + sm.readChar(); + break; + case 4: + sm.readInt(); + break; + case 5: + sm.readLong(); + break; + case 6: + sm.readFloat(); + break; + case 7: + sm.readDouble(); + break; + case 8: + sm.readString(); + break; + case 9: + sm.readBytes(new byte[3]); + break; + } + fail("MessageFormatException was not thrown"); + } + catch (MessageFormatException e) + { + // PASS + } + sm.reset(); + } + } + public void testBooleanConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeBoolean(true); + bm.reset(); + String result = bm.readString(); + assertEquals("true", result); + bm.reset(); + checkConversionsFail(bm, new int[]{1,2,3,4,5,6,7,9}); + } + + public void testByteConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeByte((byte) 43); + bm.reset(); + assertEquals(43, bm.readShort()); + bm.reset(); + assertEquals(43, bm.readInt()); + bm.reset(); + assertEquals(43, bm.readLong()); + bm.reset(); + String result = bm.readString(); + assertEquals("43", result); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 3, 6, 7, 9}); + } + + public void testShortConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeShort((short) 87); + bm.reset(); + assertEquals(87, bm.readInt()); + bm.reset(); + assertEquals(87, bm.readLong()); + bm.reset(); + assertEquals("87", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 3, 6, 7, }); + } + + public void testCharConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeChar('d'); + bm.reset(); + assertEquals("d", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 2, 4, 5, 6, 7, 9}); + } + + public void testIntConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeInt(167); + bm.reset(); + assertEquals(167, bm.readLong()); + bm.reset(); + assertEquals("167", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 2, 3, 6, 7, 9}); + } + + public void testLongConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeLong(1678); + bm.reset(); + assertEquals("1678", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 6, 7, 9}); + } + + public void testFloatConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeFloat(6.2f); + bm.reset(); + assertEquals(6.2d, bm.readDouble(), 0.01); + bm.reset(); + assertEquals("6.2", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 5, 9}); + } + + public void testDoubleConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeDouble(88.35d); + bm.reset(); + assertEquals("88.35", bm.readString()); + bm.reset(); + checkConversionsFail(bm, new int[]{0, 1, 2, 3, 4, 5, 6, 9}); + } + + public void testStringConversions() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeString("true"); + bm.reset(); + assertEquals(true, bm.readBoolean()); + bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeString("2"); + bm.reset(); + assertEquals((byte)2, bm.readByte()); + bm.reset(); + assertEquals((short)2, bm.readShort()); + bm.reset(); + assertEquals(2, bm.readInt()); + bm.reset(); + assertEquals((long)2, bm.readLong()); + bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeString("5.7"); + bm.reset(); + assertEquals(5.7f, bm.readFloat()); + bm.reset(); + assertEquals(5.7d, bm.readDouble()); + } + + public void testNulls() throws Exception + { + JMSStreamMessage bm = TestMessageHelper.newJMSStreamMessage(); + bm.writeString(null); + bm.writeObject(null); + bm.reset(); + assertNull(bm.readObject()); + assertNull(bm.readObject()); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(StreamMessageTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java new file mode 100644 index 0000000000..30f3b0b4eb --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java @@ -0,0 +1,300 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.client.message; + +import javax.jms.JMSException; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.TestMessageHelper; + +public class TextMessageTest extends TestCase +{ + public void testTextOnConstruction() throws Exception + { + JMSTextMessage tm = TestMessageHelper.newJMSTextMessage(); + tm.setText("pies"); + String val = tm.getText(); + assertEquals(val, "pies"); + } + + public void testClearBody() throws Exception + { + JMSTextMessage tm = TestMessageHelper.newJMSTextMessage(); + tm.setText("pies"); + tm.clearBody(); + String val = tm.getText(); + assertNull(val); + tm.setText("Banana"); + val = tm.getText(); + assertEquals(val, "Banana"); + } + + + public void testBooleanPropertyLookup() + { + try + { + JMSTextMessage tm = TestMessageHelper.newJMSTextMessage(); + + tm.setBooleanProperty("value", true); + Assert.assertEquals(true, tm.getBooleanProperty("value")); + Assert.assertEquals("true", tm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testBytePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setByteProperty("value", Byte.MAX_VALUE); + + Assert.assertEquals(Byte.MAX_VALUE, mm.getByteProperty("value")); + Assert.assertEquals((short) Byte.MAX_VALUE, mm.getShortProperty("value")); + Assert.assertEquals(Byte.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Byte.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Byte.MAX_VALUE, mm.getStringProperty("value")); + + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testShortPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setShortProperty("value", Short.MAX_VALUE); + Assert.assertEquals(Short.MAX_VALUE, mm.getShortProperty("value")); + Assert.assertEquals((int) Short.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Short.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Short.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testDoublePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setDoubleProperty("value", Double.MAX_VALUE); + Assert.assertEquals(Double.MAX_VALUE, mm.getDoubleProperty("value")); + Assert.assertEquals("" + Double.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFloatPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setFloatProperty("value", Float.MAX_VALUE); + Assert.assertEquals(Float.MAX_VALUE, mm.getFloatProperty("value")); + Assert.assertEquals((double) Float.MAX_VALUE, mm.getDoubleProperty("value")); + Assert.assertEquals("" + Float.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testIntPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setIntProperty("value", Integer.MAX_VALUE); + Assert.assertEquals(Integer.MAX_VALUE, mm.getIntProperty("value")); + Assert.assertEquals((long) Integer.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Integer.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testLongPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.setLongProperty("value", Long.MAX_VALUE); + Assert.assertEquals(Long.MAX_VALUE, mm.getLongProperty("value")); + Assert.assertEquals("" + Long.MAX_VALUE, mm.getStringProperty("value")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + + // Failed Lookups + + public void testFailedBooleanPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + Assert.assertEquals(false, mm.getBooleanProperty("int")); + } + catch (JMSException e) + { + Assert.fail("JMSException received." + e); + } + } + + public void testFailedBytePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getByteProperty("random"); + Assert.fail("NumberFormatException expected"); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + + } + + public void testFailedDoublePropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getDoubleProperty("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedFloatPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getFloatProperty("random"); + Assert.fail("NullPointerException should be received."); + } + catch (NullPointerException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedIntPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getIntProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedLongPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getLongProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + public void testFailedShortPropertyLookup() + { + try + { + JMSMapMessage mm = TestMessageHelper.newJMSMapMessage(); + mm.getShortProperty("random"); + Assert.fail("NumberFormatException should be received."); + } + catch (NumberFormatException e) + { + //normal execution + } + catch (JMSException e) + { + Assert.fail("JMSException received:" + e); + } + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TextMessageTest.class); + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java new file mode 100644 index 0000000000..b5e7ae82b5 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java @@ -0,0 +1,140 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.message; + +import javax.jms.*; + +import junit.framework.TestCase; + +import org.apache.qpid.client.*; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.message.*; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +import java.util.Map; + + +public class MessageConverterTest extends TestCase +{ + + public static final String JMS_CORR_ID = "QPIDID_01"; + public static final int JMS_DELIV_MODE = 1; + public static final String JMS_TYPE = "test.jms.type"; + public static final Destination JMS_REPLY_TO = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME,"my.replyto"); + + protected JMSTextMessage testTextMessage; + + protected JMSMapMessage testMapMessage; + private AMQSession _session = new TestAMQSession(); + + + protected void setUp() throws Exception + { + super.setUp(); + testTextMessage = new JMSTextMessage(AMQMessageDelegateFactory.FACTORY_0_8); + + //Set Message Text + testTextMessage.setText("testTextMessage text"); + setMessageProperties(testTextMessage); + + testMapMessage = new JMSMapMessage(AMQMessageDelegateFactory.FACTORY_0_8); + testMapMessage.setString("testMapString", "testMapStringValue"); + testMapMessage.setDouble("testMapDouble", Double.MAX_VALUE); + } + + public void testSetProperties() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter(_session, (TextMessage) testTextMessage).getConvertedMessage(); + mesagePropertiesTest(testTextMessage, newMessage); + } + + public void testJMSTextMessageConversion() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter(_session, (TextMessage) testTextMessage).getConvertedMessage(); + assertEquals("Converted message text mismatch", ((JMSTextMessage) newMessage).getText(), testTextMessage.getText()); + } + + public void testJMSMapMessageConversion() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter(_session, (MapMessage) testMapMessage).getConvertedMessage(); + assertEquals("Converted map message String mismatch", ((JMSMapMessage) newMessage).getString("testMapString"), + testMapMessage.getString("testMapString")); + assertEquals("Converted map message Double mismatch", ((JMSMapMessage) newMessage).getDouble("testMapDouble"), + testMapMessage.getDouble("testMapDouble")); + + } + + public void testMessageConversion() throws Exception + { + Message newMessage = new NonQpidMessage(); + setMessageProperties(newMessage); + mesagePropertiesTest(testTextMessage, newMessage); + } + + private void setMessageProperties(Message message) throws JMSException + { + message.setJMSCorrelationID(JMS_CORR_ID); + message.setJMSDeliveryMode(JMS_DELIV_MODE); + message.setJMSType(JMS_TYPE); + message.setJMSReplyTo(JMS_REPLY_TO); + + //Add non-JMS properties + message.setStringProperty("testProp1", "testValue1"); + message.setDoubleProperty("testProp2", Double.MIN_VALUE); + } + + + private void mesagePropertiesTest(Message expectedMessage, Message actualMessage) + { + try + { + //check JMS prop values on newMessage match + assertEquals("JMS Correlation ID mismatch", expectedMessage.getJMSCorrelationID(), actualMessage.getJMSCorrelationID()); + assertEquals("JMS Delivery mode mismatch", expectedMessage.getJMSDeliveryMode(), actualMessage.getJMSDeliveryMode()); + assertEquals("JMS Type mismatch", expectedMessage.getJMSType(), actualMessage.getJMSType()); + assertEquals("JMS Reply To mismatch", expectedMessage.getJMSReplyTo(), actualMessage.getJMSReplyTo()); + + //check non-JMS standard props ok too + assertEquals("Test String prop value mismatch", expectedMessage.getStringProperty("testProp1"), + actualMessage.getStringProperty("testProp1")); + + assertEquals("Test Double prop value mismatch", expectedMessage.getDoubleProperty("testProp2"), + actualMessage.getDoubleProperty("testProp2")); + } + catch (JMSException e) + { + fail("An error occured testing the property values" + e.getCause()); + e.printStackTrace(); + } + } + + protected void tearDown() throws Exception + { + super.tearDown(); + testTextMessage = null; + } + + +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java new file mode 100644 index 0000000000..df53c796b2 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.test.unit.message; + +import java.util.Enumeration; +import java.util.Hashtable; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; + +public class NonQpidMessage implements Message +{ + private String _JMSMessageID; + private long _JMSTimestamp; + private byte[] _JMSCorrelationIDAsBytes; + private String _JMSCorrelationID; + private Destination _JMSReplyTo; + private Destination _JMSDestination; + private int _JMSDeliveryMode; + private boolean _JMSRedelivered; + private String _JMSType; + private long _JMSExpiration; + private int _JMSPriority; + private Hashtable _properties; + + public NonQpidMessage() + { + _properties = new Hashtable(); + _JMSPriority = javax.jms.Message.DEFAULT_PRIORITY; + _JMSDeliveryMode = javax.jms.Message.DEFAULT_DELIVERY_MODE; + } + + public String getJMSMessageID() throws JMSException + { + return _JMSMessageID; + } + + public void setJMSMessageID(String string) throws JMSException + { + _JMSMessageID = string; + } + + public long getJMSTimestamp() throws JMSException + { + return _JMSTimestamp; + } + + public void setJMSTimestamp(long l) throws JMSException + { + _JMSTimestamp = l; + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException + { + return _JMSCorrelationIDAsBytes; + } + + public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException + { + _JMSCorrelationIDAsBytes = bytes; + } + + public void setJMSCorrelationID(String string) throws JMSException + { + _JMSCorrelationID = string; + } + + public String getJMSCorrelationID() throws JMSException + { + return _JMSCorrelationID; + } + + public Destination getJMSReplyTo() throws JMSException + { + return _JMSReplyTo; + } + + public void setJMSReplyTo(Destination destination) throws JMSException + { + _JMSReplyTo = destination; + } + + public Destination getJMSDestination() throws JMSException + { + return _JMSDestination; + } + + public void setJMSDestination(Destination destination) throws JMSException + { + _JMSDestination = destination; + } + + public int getJMSDeliveryMode() throws JMSException + { + return _JMSDeliveryMode; + } + + public void setJMSDeliveryMode(int i) throws JMSException + { + _JMSDeliveryMode = i; + } + + public boolean getJMSRedelivered() throws JMSException + { + return _JMSRedelivered; + } + + public void setJMSRedelivered(boolean b) throws JMSException + { + _JMSRedelivered = b; + } + + public String getJMSType() throws JMSException + { + return _JMSType; + } + + public void setJMSType(String string) throws JMSException + { + _JMSType = string; + } + + public long getJMSExpiration() throws JMSException + { + return _JMSExpiration; + } + + public void setJMSExpiration(long l) throws JMSException + { + _JMSExpiration = l; + } + + public int getJMSPriority() throws JMSException + { + return _JMSPriority; + } + + public void setJMSPriority(int i) throws JMSException + { + _JMSPriority = i; + } + + public void clearProperties() throws JMSException + { + _properties.clear(); + } + + public boolean propertyExists(String string) throws JMSException + { + return _properties.containsKey(string); + } + + public boolean getBooleanProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Boolean) + { + return (Boolean) o; + } + else + { + return Boolean.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public byte getByteProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Byte) + { + return (Byte) o; + } + else + { + return Byte.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public short getShortProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Short) + { + return (Short) o; + } + else + { + return Short.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public int getIntProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Integer) + { + return (Integer) o; + } + else + { + return Integer.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public long getLongProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Long) + { + return (Long) o; + } + else + { + return Long.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public float getFloatProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Float) + { + return (Float) o; + } + else + { + return Float.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public double getDoubleProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Double) + { + return (Double) o; + } + else + { + return Double.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public String getStringProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof String) + { + return (String) o; + } + else + { + return null; + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public Object getObjectProperty(String string) throws JMSException + { + if (propertyExists(string)) + { + Object o = _properties.get(string); + if (o instanceof Boolean) + { + return (Boolean) o; + } + else + { + return Boolean.valueOf(null); + } + } + else + { + throw new JMSException("property does not exist: " + string); + } + } + + public Enumeration getPropertyNames() throws JMSException + { + return _properties.keys(); + } + + public void setBooleanProperty(String string, boolean b) throws JMSException + { + _properties.put(string, b); + } + + public void setByteProperty(String string, byte b) throws JMSException + { + _properties.put(string, b); + } + + public void setShortProperty(String string, short i) throws JMSException + { + _properties.put(string, i); + } + + public void setIntProperty(String string, int i) throws JMSException + { + _properties.put(string, i); + } + + public void setLongProperty(String string, long l) throws JMSException + { + _properties.put(string, l); + } + + public void setFloatProperty(String string, float v) throws JMSException + { + _properties.put(string, v); + } + + public void setDoubleProperty(String string, double v) throws JMSException + { + _properties.put(string, v); + } + + public void setStringProperty(String string, String string1) throws JMSException + { + _properties.put(string, string1); + } + + public void setObjectProperty(String string, Object object) throws JMSException + { + _properties.put(string, object); + } + + public void acknowledge() throws JMSException + { + + } + + public void clearBody() throws JMSException + { + + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java new file mode 100644 index 0000000000..a881f6a822 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/message/TestAMQSession.java @@ -0,0 +1,171 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.message; + +import org.apache.qpid.client.*; +import org.apache.qpid.client.message.AMQMessageDelegateFactory; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.AMQException; + +import javax.jms.*; +import java.util.Map; + +public class TestAMQSession extends AMQSession +{ + + public TestAMQSession() + { + super(null, 0, false, AUTO_ACKNOWLEDGE, null, 0, 0); + } + + public void acknowledgeMessage(long deliveryTag, boolean multiple) + { + + } + + public void sendQueueBind(AMQShortString queueName, AMQShortString routingKey, FieldTable arguments, AMQShortString exchangeName, AMQDestination destination) throws AMQException, FailoverException + { + + } + + public void sendClose(long timeout) throws AMQException, FailoverException + { + + } + + public void sendCommit() throws AMQException, FailoverException + { + + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException + { + return null; + } + + public void sendCreateQueue(AMQShortString name, boolean autoDelete, boolean durable, boolean exclusive, Map arguments) throws AMQException, FailoverException + { + + } + + public TemporaryQueue createTemporaryQueue() throws JMSException + { + return null; + } + + protected void sendRecover() throws AMQException, FailoverException + { + + } + + public void rejectMessage(long deliveryTag, boolean requeue) + { + + } + + public void releaseForRollback() + { + + } + + public void sendRollback() throws AMQException, FailoverException + { + + } + + public BasicMessageConsumer_0_8 createMessageConsumer(AMQDestination destination, int prefetchHigh, int prefetchLow, boolean noLocal, boolean exclusive, String selector, FieldTable arguments, boolean noConsume, boolean autoClose) throws JMSException + { + return null; + } + + public boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName, AMQShortString routingKey) throws JMSException + { + return false; + } + + public boolean isQueueBound(AMQDestination destination) throws JMSException + { + return false; + } + + public void sendConsume(BasicMessageConsumer_0_8 consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector, int tag) throws AMQException, FailoverException + { + + } + + public BasicMessageProducer_0_8 createMessageProducer(Destination destination, boolean mandatory, boolean immediate, boolean waitUntilSent, long producerId) + { + return null; + } + + protected Long requestQueueDepth(AMQDestination amqd) throws AMQException, FailoverException + { + return null; + } + + public void sendExchangeDeclare(AMQShortString name, AMQShortString type, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException, FailoverException + { + + } + + public void sendQueueDeclare(AMQDestination amqd, AMQProtocolHandler protocolHandler) throws AMQException, FailoverException + { + + } + + public void sendQueueDelete(AMQShortString queueName) throws AMQException, FailoverException + { + + } + + public void sendSuspendChannel(boolean suspend) throws AMQException, FailoverException + { + + } + + protected boolean tagLE(long tag1, long tag2) + { + return false; + } + + protected boolean updateRollbackMark(long current, long deliveryTag) + { + return false; + } + + public AMQMessageDelegateFactory getMessageDelegateFactory() + { + return AMQMessageDelegateFactory.FACTORY_0_8; + } + + protected Object getFailoverMutex() + { + return this; + } + + public void checkNotClosed() + { + + } +} diff --git a/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/tests.properties b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/tests.properties new file mode 100644 index 0000000000..2fd961a078 --- /dev/null +++ b/RC6/qpid/java/client/src/test/java/org/apache/qpid/test/unit/tests.properties @@ -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. + + +java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory + +# use the following property to configure the default connector +#java.naming.provider.url - ignored. + +# register some connection factories +# connectionfactory.[jndiname] = [ConnectionURL] +connectionfactory.local = amqp://username:password@clientid/test?brokerlist='tcp://localhost:5672' +#qpid:password=guest;username=guest;client_id=clientid;virtualhost=test@tcp:127.0.0.1:5672 + + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue +queue.queue = example.queue +queue.xaQueue = xaQueue + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +#topic.ibmStocks = stocks.nyse.ibm +topic.xaTopic = xaTopic +topic.durableSubscriberTopic = durableSubscriberTopic + +# Register an AMQP destination in JNDI +# NOTE: Qpid currently only supports direct,topics and headers +# destination.[jniName] = [BindingURL] +#destination.direct = direct://amq.direct//directQueue diff --git a/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.bat b/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.bat new file mode 100644 index 0000000000..eb6a87fa9e --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.bat @@ -0,0 +1,69 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-JNDI-Setup.bat" +set JAVACLASS= + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist='localhost' amq.ConnectionFactory +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist='vm://:1' amq.VMConnectionFactory +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindQueue amq.Queue direct://amq.direct//IBMPerfQueue1 +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic1 topic://amq.topic/IBMPerfTopic1/ +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic2 topic://amq.topic/IBMPerfTopic2/ +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic3 topic://amq.topic/IBMPerfTopic3/ + + + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.sh b/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.sh new file mode 100755 index 0000000000..e3112f812d --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-JNDI-Setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist=\'tcp://localhost\' amq.ConnectionFactory +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindConnectionFactory amqp://guest:guest@clientid/testpath?brokerlist=\'vm://:1\' amq.VMConnectionFactory +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindQueue amq.Queue direct://amq.direct//IBMPerfQueue1 +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic1 topic://amq.topic/IBMPerfTopic1/ +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic2 topic://amq.topic/IBMPerfTopic2/ +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic3 topic://amq.topic/IBMPerfTopic3/ +qpid-run org.apache.qpid.IBMPerfTest.JNDIBindTopic amq.Topic4 topic://amq.topic/IBMPerfTopic4/ \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Publisher.bat b/RC6/qpid/java/client/test/bin/IBM-Publisher.bat new file mode 100644 index 0000000000..5bb4343c4c --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Publisher.bat @@ -0,0 +1,62 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-Publisher.bat" +set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Publisher -nt 4 %* + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS% + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Publisher.sh b/RC6/qpid/java/client/test/bin/IBM-Publisher.sh new file mode 100755 index 0000000000..adecf040bc --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Publisher.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export MSGSIZE=100 +qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Publisher -nt 4 $* \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-PutGet.bat b/RC6/qpid/java/client/test/bin/IBM-PutGet.bat new file mode 100644 index 0000000000..c4316f1256 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-PutGet.bat @@ -0,0 +1,62 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-PutGet.bat" +set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.PutGet -nt 6 %* + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS% + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-PutGet.sh b/RC6/qpid/java/client/test/bin/IBM-PutGet.sh new file mode 100755 index 0000000000..c75667c9f6 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-PutGet.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.PutGet -nt 6 $* \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-README.txt b/RC6/qpid/java/client/test/bin/IBM-README.txt new file mode 100644 index 0000000000..b076f3b3ca --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-README.txt @@ -0,0 +1,19 @@ +The IBM JMS Performance Harness scripts have take the following additional parameters + +-tx : Enable transactions +-pp : Enable persistent messaging + +-ms 1000 : Set message size (default 1000 bytes) +-cc 1 : Number of messages to per commit (default 1) Only applies to Sender/Subscriber + +The IBM JMS Performance Harness will need to be downloaded and the library added to client/test/lib. + +The Library can be found here: + +http://www.alphaworks.ibm.com/tech/perfharness + +Before running the required test the IBM JNDI Setup script should be run. + +This will create a filesystem based JNDI Context located at: + +System.properties{java.io.tmpdir}/IBMPerfTestsJNDI/ \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Receiver.bat b/RC6/qpid/java/client/test/bin/IBM-Receiver.bat new file mode 100644 index 0000000000..dff44d472a --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Receiver.bat @@ -0,0 +1,62 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-Receiver.bat" +set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Receiver %* + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS% + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Receiver.sh b/RC6/qpid/java/client/test/bin/IBM-Receiver.sh new file mode 100755 index 0000000000..f50f0f744e --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Receiver.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export MSGSIZE=100 +qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Receiver $* \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Sender.bat b/RC6/qpid/java/client/test/bin/IBM-Sender.bat new file mode 100644 index 0000000000..b8826322e5 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Sender.bat @@ -0,0 +1,62 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-Sender.bat" +set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Sender -ms $MSGSIZE -mg 1000000 %* + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS% + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Sender.sh b/RC6/qpid/java/client/test/bin/IBM-Sender.sh new file mode 100755 index 0000000000..b99429fd54 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Sender.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export MSGSIZE=100 +qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Queue -tc jms.r11.Sender -ms $MSGSIZE -mg 1000000 $* \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Subscriber.bat b/RC6/qpid/java/client/test/bin/IBM-Subscriber.bat new file mode 100644 index 0000000000..5245639eba --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Subscriber.bat @@ -0,0 +1,62 @@ +@REM +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM + +@echo off +REM Script to run the Qpid Java Broker + +set CMD="IBM-Subscriber.bat" +set JAVACLASS=JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:/C:/temp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Subscriber -nt 4 %* + +rem Guess QPID_HOME if not defined +set CURRENT_DIR=%cd% +if not "%QPID_HOME%" == "" goto gotHome +set QPID_HOME=%CURRENT_DIR% +echo %QPID_HOME% +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +cd .. +set QPID_HOME=%cd% +cd %CURRENT_DIR% +:gotHome +if exist "%QPID_HOME%\bin\%CMD%" goto okHome +echo The QPID_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program +goto end +:okHome + +if not "%JAVA_HOME%" == "" goto gotJavaHome +echo The JAVA_HOME environment variable is not defined +echo This environment variable is needed to run this program +goto exit +:gotJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +goto okJavaHome +:noJavaHome +echo The JAVA_HOME environment variable is not defined correctly +echo This environment variable is needed to run this program. +goto exit +:okJavaHome + +set CLIENT_TEST_CLASSES=%QPID_HOME%\lib\client-test-launch.jar + +echo on +"%JAVA_HOME%\bin\java" -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%CLIENT_TEST_CLASSES%" %JAVACLASS% + +:end + +pause \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/IBM-Subscriber.sh b/RC6/qpid/java/client/test/bin/IBM-Subscriber.sh new file mode 100755 index 0000000000..43550100be --- /dev/null +++ b/RC6/qpid/java/client/test/bin/IBM-Subscriber.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export MSGSIZE=100 +qpid-run JMSPerfHarness -pc JNDI -ii com.sun.jndi.fscontext.RefFSContextFactory -iu file:///tmp/IBMPerfTestsJNDI/ -cf amq.ConnectionFactory -d amq.Topic -db 1 -dx 4 -tc jms.r11.Subscriber -nt 4 $* \ No newline at end of file diff --git a/RC6/qpid/java/client/test/bin/headersListener.sh b/RC6/qpid/java/client/test/bin/headersListener.sh new file mode 100755 index 0000000000..81930b7043 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/headersListener.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.headers.Listener $* diff --git a/RC6/qpid/java/client/test/bin/headersListenerGroup.sh b/RC6/qpid/java/client/test/bin/headersListenerGroup.sh new file mode 100755 index 0000000000..e1cc05cfd2 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/headersListenerGroup.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +for i; do + ./headersListener.sh -host 10.0.0.1 -port 5672 >$i.out 2>$i.err & + echo $! > $i.pid +done; diff --git a/RC6/qpid/java/client/test/bin/headersPublisher.sh b/RC6/qpid/java/client/test/bin/headersPublisher.sh new file mode 100755 index 0000000000..fd9fd26416 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/headersPublisher.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.headers.Publisher $* diff --git a/RC6/qpid/java/client/test/bin/run_many.sh b/RC6/qpid/java/client/test/bin/run_many.sh new file mode 100755 index 0000000000..cca2ffec21 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/run_many.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# args: +# +# +# + +for i in `seq 1 $1`; do + $3 >$2.$i.out 2>>$2.err & + echo $! > $2.$i.pid +done; diff --git a/RC6/qpid/java/client/test/bin/serviceProvidingClient.sh b/RC6/qpid/java/client/test/bin/serviceProvidingClient.sh new file mode 100755 index 0000000000..cbcf5a0f4b --- /dev/null +++ b/RC6/qpid/java/client/test/bin/serviceProvidingClient.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +PROFILE=$1 +shift +# XXX -Xms1024m -XX:NewSize=300m +. qpid-run org.apache.qpid.requestreply1.ServiceProvidingClient $1 guest guest /test serviceQ diff --git a/RC6/qpid/java/client/test/bin/serviceRequestingClient.sh b/RC6/qpid/java/client/test/bin/serviceRequestingClient.sh new file mode 100755 index 0000000000..213f44c00b --- /dev/null +++ b/RC6/qpid/java/client/test/bin/serviceRequestingClient.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +PROFILE=$1 +shift +thehosts=$1 +shift +echo $thehosts +# XXX -Xms1024m -XX:NewSize=300m +. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.requestreply1.ServiceRequestingClient $thehosts guest guest /test serviceQ "$@" diff --git a/RC6/qpid/java/client/test/bin/testService.sh b/RC6/qpid/java/client/test/bin/testService.sh new file mode 100755 index 0000000000..20161c3abf --- /dev/null +++ b/RC6/qpid/java/client/test/bin/testService.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +. qpid-run org.apache.qpid.requestreply1.TestService 192.168.55.63 5672 foo x x diff --git a/RC6/qpid/java/client/test/bin/topicListener.sh b/RC6/qpid/java/client/test/bin/topicListener.sh new file mode 100755 index 0000000000..ac0cb63c91 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/topicListener.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# XXX -Xmx512m -Xms512m -XX:NewSize=150m +. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.topic.Listener $* diff --git a/RC6/qpid/java/client/test/bin/topicPublisher.sh b/RC6/qpid/java/client/test/bin/topicPublisher.sh new file mode 100755 index 0000000000..e35c131fe8 --- /dev/null +++ b/RC6/qpid/java/client/test/bin/topicPublisher.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# XXX -Xmx512m -Xms512m -XX:NewSize=150m +. qpid-run -Damqj.logging.level="INFO" org.apache.qpid.topic.Publisher $* diff --git a/RC6/qpid/java/client/test/etc/ApacheDS.properties b/RC6/qpid/java/client/test/etc/ApacheDS.properties new file mode 100644 index 0000000000..6c5cb4cec4 --- /dev/null +++ b/RC6/qpid/java/client/test/etc/ApacheDS.properties @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Standard JNDI properties +java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory +java.naming.provider.url=ldap://localhost:389/ou=system +java.naming.security.authentication=simple +java.naming.security.principal=uid=admin,ou=system +java.naming.security.credentials=secret diff --git a/RC6/qpid/java/client/test/example_build.xml b/RC6/qpid/java/client/test/example_build.xml new file mode 100644 index 0000000000..a12862be04 --- /dev/null +++ b/RC6/qpid/java/client/test/example_build.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RC6/qpid/java/common.xml b/RC6/qpid/java/common.xml new file mode 100644 index 0000000000..146781ec23 --- /dev/null +++ b/RC6/qpid/java/common.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ant build + + This target compiles all sources, creates java archives, and + copies scripts and configurations into the build directory: + + ${build} + + The build directory is treated as a live distro which this target + will incrementally update. Developers can put the build/bin + directory into their path in order to run any scripts or code + directly out of the live build: + + ${build.bin} + + ant test [ -Dtest=<pattern> ] [-Dprofile=<profile>] [ report ] + + Execute unit tests and place the output in the build results + directory: + + ${build.results} + + All test output will be redirected to a file of the form: + + TEST-<class>.txt + + The same output is available using an xml file format: + + TEST-<class>.xml + + The test system property may be used to restrict the number of + tests run by a given invocation of the ant test target. The + following command will run just the MongooseTest test case: + + ant test -Dtest=MongooseTest + + In addition, patterns may be used to specify more than one test. + The following command will run both the MongooseTest and GooseTest + test cases: + + ant test -Dtest=*ooseTest + + If no test property is specified, the "ant test" target will + default to running all available tests for the project or module + depending on the current working directory. + + Test Reports + + It can be useful to append the report target in order to + generate an html summary of the tests that were just run. The + following command will run both the MongooseTest and GooseTest + test cases and generate an html summary of the results: + + ant test -Dtest=*ooseTest report + + See the documentation for the "ant report" target for more details + on the generated report. + + Test Profiles + + There are a number of profiles defined for running the test suite. + These test profiles define how the test should be run. If the test + profile is not specified then 'default.testprofile' is utilised. + This runs the system tests against the Java InVM broker. Additional + test profiles exists as follows: + + cpp : Runs against the built cpp tree broker. + + ant report + + The report target will generate an html summary of the current + test results into the report directory: + + ${build.report} + + The report target will operate on all results currently in the + build results directory. Results are not cleared unless the clean + target is used. This means that several consecutive test runs can + be summarized into a single report. The following commands will + produce a report summarizing both the MongooseTest and GooseTest + test cases: + + ant test -Dtest=MongooseTest + ... + ant test -Dtest=GooseTest + ... + ant report + + ant release + + The release target generates binary distribution archives and + places them into the release directory: + + ${release} + + ant release-bin + + The release-bin target generates binary distribution archives for + modules that have a specific binary package configured. + To enable for other modules create a target "release-bin" that + depends on target "release-bin-tasks". The output is placed in: + + ${module.release} + + ant clean + + The clean target removes build artifacts. When invoked from the + project root this target will remove the entire project build and + release directories: + + ${build} + and + ${release} + and + ${module.release} + + When invoked from a specific module, the clean target will delete + that modules build root from underneath the project build root: + + ${build}/<module> + + ant clean-results + + The clean-results target removes all test output from the test + results directory: + + ${build.results} + + + + diff --git a/RC6/qpid/java/common/Composite.tpl b/RC6/qpid/java/common/Composite.tpl new file mode 100644 index 0000000000..17cf846d8c --- /dev/null +++ b/RC6/qpid/java/common/Composite.tpl @@ -0,0 +1,336 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.transport.codec.Decoder; +import org.apache.qpid.transport.codec.Encodable; +import org.apache.qpid.transport.codec.Encoder; + +import org.apache.qpid.transport.network.Frame; + +import org.apache.qpid.util.Strings; + + +${ +from genutil import * + +cls = klass(type)["@name"] + +segments = type["segments"] + +if type.name in ("control", "command"): + base = "Method" + size = 0 + pack = 2 + if segments: + payload = "true" + else: + payload = "false" + if type.name == "control" and cls == "connection": + track = "Frame.L1" + elif cls == "session" and type["@name"] in ("attach", "attached", "detach", "detached"): + track = "Frame.L2" + elif type.name == "command": + track = "Frame.L4" + else: + track = "Frame.L3" +else: + base = "Struct" + size = type["@size"] + pack = num(type["@pack"]) + payload = "false" + track = "-1" + +PACK_TYPES = { + 1: "byte", + 2: "short", + 4: "int" +} + +typecode = code(type) +} + +public final class $name extends $base { + + public static final int TYPE = $typecode; + + public final int getStructType() { + return TYPE; + } + + public final int getSizeWidth() { + return $size; + } + + public final int getPackWidth() { + return $pack; + } + + public final boolean hasPayload() { + return $payload; + } + + public final byte getEncodedTrack() { + return $track; + } + +${ + +if pack > 0: + out(" private $(PACK_TYPES[pack]) packing_flags = 0;\n"); + +fields = get_fields(type) +params = get_parameters(type, fields) +options = get_options(fields) + +for f in fields: + if not f.empty: + out(" private $(f.type) $(f.name);\n") + +if segments: + out(" private Header header;\n") + out(" private ByteBuffer body;\n") +} + +${ +if fields: + out(" public $name() {}\n") +} + + public $name($(", ".join(params))) { +${ +for f in fields: + if f.option: continue + out(" $(f.set)($(f.name));\n") + +if segments: + out(" setHeader(header);\n") + out(" setBody(body);\n") + +if options or base == "Method": + out(""" + for (int i=0; i < _options.length; i++) { + switch (_options[i]) { +""") + + for f in options: + out(" case $(f.option): packing_flags |= $(f.flag_mask(pack)); break;\n") + + if base == "Method": + out(""" case SYNC: this.setSync(true); break; + case BATCH: this.setBatch(true); break; +""") + out(""" case NONE: break; + default: throw new IllegalArgumentException("invalid option: " + _options[i]); + } + } +""") +} + } + +${ + +if base == "Method": + out(""" public void dispatch(C context, MethodDelegate delegate) { + delegate.$(dromedary(name))(context, this); + }""") +} + +${ +for f in fields: + if pack > 0: + out(""" + public final boolean $(f.has)() { + return (packing_flags & $(f.flag_mask(pack))) != 0; + } + + public final $name $(f.clear)() { + packing_flags &= ~$(f.flag_mask(pack)); +${ +if not f.empty: + out(" this.$(f.name) = $(f.default);") +} + this.dirty = true; + return this; + } +""") + + out(""" + public final $(f.type) $(f.get)() { +${ +if f.empty: + out(" return $(f.has)();") +else: + out(" return $(f.name);") +} + } + + public final $name $(f.set)($(f.type) value) { +${ +if not f.empty: + out(" this.$(f.name) = value;") +} +${ +if pack > 0: + if f.empty: + out(" if (value)\\n") + out(" packing_flags |= $(f.flag_mask(pack));\\n") + out(" else\\n") + out(" packing_flags &= ~$(f.flag_mask(pack));") + else: + out(" packing_flags |= $(f.flag_mask(pack));") +} + this.dirty = true; + return this; + } + + public final $name $(f.name)($(f.type) value) { + return $(f.set)(value); + } +""") +} + +${ +if segments: + out(""" public final Header getHeader() { + return this.header; + } + + public final void setHeader(Header header) { + this.header = header; + } + + public final $name header(Header header) { + setHeader(header); + return this; + } + + public final ByteBuffer getBody() { + if (this.body == null) + { + return null; + } + else + { + return this.body.slice(); + } + } + + public final void setBody(ByteBuffer body) { + this.body = body; + } + + public final $name body(ByteBuffer body) + { + setBody(body); + return this; + } + + public final byte[] getBodyBytes() { + ByteBuffer buf = getBody(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + return bytes; + } + + public final void setBody(byte[] body) + { + setBody(ByteBuffer.wrap(body)); + } + + public final String getBodyString() { + return Strings.fromUTF8(getBodyBytes()); + } + + public final void setBody(String body) { + setBody(Strings.toUTF8(body)); + } +""") +} + + public void write(Encoder enc) + { +${ +if pack > 0: + out(" enc.writeUint%s(packing_flags);\n" % (pack*8)); + +for f in fields: + if f.empty: + continue + if pack > 0: + out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ") + pre = "" + post = "" + if f.type_node.name == "struct": + pre = "%s.TYPE, " % cname(f.type_node) + elif f.type_node.name == "domain": + post = ".getValue()" + out(" enc.write$(f.coder)($(pre)this.$(f.name)$(post));\n") +} + } + + public void read(Decoder dec) + { +${ +if pack > 0: + out(" packing_flags = ($(PACK_TYPES[pack])) dec.readUint%s();\n" % (pack*8)); + +for f in fields: + if f.empty: + continue + if pack > 0: + out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ") + pre = "" + post = "" + arg = "" + if f.type_node.name == "struct": + pre = "(%s)" % cname(f.type_node) + arg = "%s.TYPE" % cname(f.type_node) + elif f.type_node.name == "domain": + pre = "%s.get(" % cname(f.type_node) + post = ")" + out(" this.$(f.name) = $(pre)dec.read$(f.coder)($(arg))$(post);\n") +} + } + + public Map getFields() + { + Map result = new LinkedHashMap(); + +${ +for f in fields: + if pack > 0: + out(" if ((packing_flags & $(f.flag_mask(pack))) != 0)\n ") + out(' result.put("$(f.name)", $(f.get)());\n') +} + + return result; + } + +} diff --git a/RC6/qpid/java/common/Constant.tpl b/RC6/qpid/java/common/Constant.tpl new file mode 100644 index 0000000000..da4233c847 --- /dev/null +++ b/RC6/qpid/java/common/Constant.tpl @@ -0,0 +1,35 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +${from genutil import *} + +public interface Constant +{ +${ +constants = spec.query["amqp/constant"] + +for c in constants: + name = scream(c["@name"]) + value = c["@value"] + out(" public static final int $name = $value;\n") +}} diff --git a/RC6/qpid/java/common/Enum.tpl b/RC6/qpid/java/common/Enum.tpl new file mode 100644 index 0000000000..0835d34a20 --- /dev/null +++ b/RC6/qpid/java/common/Enum.tpl @@ -0,0 +1,57 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 enum $name { +${ +from genutil import * + +vtype = jtype(resolve_type(type)) + +choices = [(scream(ch["@name"]), "(%s) %s" % (vtype, ch["@value"])) + for ch in type.query["enum/choice"]] +} + $(",\n ".join(["%s(%s)" % ch for ch in choices])); + + private final $vtype value; + + $name($vtype value) + { + this.value = value; + } + + public $vtype getValue() + { + return value; + } + + public static $name get($vtype value) + { + switch (value) + { +${ +for ch, value in choices: + out(' case $value: return $ch;\n') +} default: throw new IllegalArgumentException("no such value: " + value); + } + } +} diff --git a/RC6/qpid/java/common/Invoker.tpl b/RC6/qpid/java/common/Invoker.tpl new file mode 100644 index 0000000000..2eed43ad28 --- /dev/null +++ b/RC6/qpid/java/common/Invoker.tpl @@ -0,0 +1,73 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public abstract class $(invoker) { +${ +from genutil import * + +results = False + +for c in composites: + name = cname(c) + fields = get_fields(c) + params = get_parameters(c, fields) + args = get_arguments(c, fields) + result = c["result"] + if result: + results = True + if not result["@type"]: + rname = cname(result["struct"]) + else: + rname = cname(result, "@type") + jresult = "Future<%s>" % rname + jreturn = "return " + jclass = ", %s.class" % rname + else: + jresult = "void" + jreturn = "" + jclass = "" + + if c.name == "command": + access = "public " + else: + access = "" + + out(""" + $(access)final $jresult $(dromedary(name))($(", ".join(params))) { + $(jreturn)invoke(new $name($(", ".join(args)))$jclass); + } +""") +} + protected abstract void invoke(Method method); +${ +if results: + out(""" + protected abstract Future invoke(Method method, Class resultClass); +""") +} +} diff --git a/RC6/qpid/java/common/MethodDelegate.tpl b/RC6/qpid/java/common/MethodDelegate.tpl new file mode 100644 index 0000000000..27e20a7ef2 --- /dev/null +++ b/RC6/qpid/java/common/MethodDelegate.tpl @@ -0,0 +1,37 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 abstract class MethodDelegate { + + public abstract void handle(C context, Method method); +${ +from genutil import * + +for c in composites: + name = cname(c) + out(""" + public void $(dromedary(name))(C context, $name method) { + handle(context, method); + }""") +} +} diff --git a/RC6/qpid/java/common/Option.tpl b/RC6/qpid/java/common/Option.tpl new file mode 100644 index 0000000000..776b211ad5 --- /dev/null +++ b/RC6/qpid/java/common/Option.tpl @@ -0,0 +1,41 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 enum Option { + +${ +from genutil import * + +options = {} + +for c in composites: + for f in c.query["field"]: + t = resolve_type(f) + if t["@name"] == "bit": + option = scream(f["@name"]) + if not options.has_key(option): + options[option] = None + out(" $option,\n")} + BATCH, + NONE +} diff --git a/RC6/qpid/java/common/StructFactory.tpl b/RC6/qpid/java/common/StructFactory.tpl new file mode 100644 index 0000000000..09c669f74e --- /dev/null +++ b/RC6/qpid/java/common/StructFactory.tpl @@ -0,0 +1,60 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 StructFactory { + + public static Struct create(int type) + { + switch (type) + { +${ +from genutil import * + +fragment = """ case $name.TYPE: + return new $name(); +""" + +for c in composites: + name = cname(c) + if c.name == "struct": + out(fragment) +} default: + throw new IllegalArgumentException("type: " + type); + } + } + + public static Struct createInstruction(int type) + { + switch (type) + { +${ +for c in composites: + name = cname(c) + if c.name in ("command", "control"): + out(fragment) +} default: + throw new IllegalArgumentException("type: " + type); + } + } + +} diff --git a/RC6/qpid/java/common/Type.tpl b/RC6/qpid/java/common/Type.tpl new file mode 100644 index 0000000000..7f9cfee268 --- /dev/null +++ b/RC6/qpid/java/common/Type.tpl @@ -0,0 +1,84 @@ +package org.apache.qpid.transport; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +${from genutil import *} + +public enum Type +{ + +${ +types = spec.query["amqp/type"] + spec.query["amqp/class/type"] +codes = {} +first = True +for t in types: + code = t["@code"] + fix_width = t["@fixed-width"] + var_width = t["@variable-width"] + + if code is None: + continue + + if fix_width is None: + width = var_width + fixed = "false" + else: + width = fix_width + fixed = "true" + + name = scream(t["@name"]) + codes[code] = name + + if first: + first = False + else: + out(",\n") + + out(" $name((byte) $code, $width, $fixed)") +}; + + public byte code; + public int width; + public boolean fixed; + + Type(byte code, int width, boolean fixed) + { + this.code = code; + this.width = width; + this.fixed = fixed; + } + + public static Type get(byte code) + { + switch (code) + { +${ +keys = list(codes.keys()) +keys.sort() + +for code in keys: + out(" case (byte) $code: return $(codes[code]);\n") +} + default: return null; + } + } +} diff --git a/RC6/qpid/java/common/bin/qpid-run b/RC6/qpid/java/common/bin/qpid-run new file mode 100755 index 0000000000..0b5070d937 --- /dev/null +++ b/RC6/qpid/java/common/bin/qpid-run @@ -0,0 +1,264 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Test if we're running on cygwin. +cygwin=false +if [[ "$(uname -a | fgrep Cygwin)" != "" ]]; then + cygwin=true +fi + +die() { + if [[ $1 = -usage ]]; then + shift + usage=true + else + usage=false + fi + echo "$@" + $usage && echo + $usage && usage + exit 1 +} + +OFF=0 +WARN=1 +INFO=2 + +if [ -z "$QPID_RUN_LOG" ]; then + QPID_RUN_LOG=$OFF +fi + +log() { + if [ "$1" -le "$QPID_RUN_LOG" ]; then + shift + echo "$@" + fi +} + +if [ -z $AMQJ_LOGGING_LEVEL ]; then + export AMQJ_LOGGING_LEVEL=info +fi + +#Set to help us get round the manifold problems of ps/pgrep on various +#platforms which gather up to prevent qpid_stop from working ..... +if [ -z "$QPID_PNAME" ]; then + export QPID_PNAME=" -DPNAME=QPBRKR" +fi + +if [ -z "$QPID_HOME" ]; then + export QPID_HOME=$(dirname $(dirname $(readlink -f $0))) + export PATH=${PATH}:${QPID_HOME}/bin +fi + +if [ -z "$QPID_WORK" ]; then + log $INFO Setting QPID_WORK to $HOME as default + QPID_WORK=$HOME +fi + +if [ -z "$JAVA" ]; then + JAVA=java +fi + +if $cygwin; then + QPID_HOME=$(cygpath -w $QPID_HOME) + QPID_WORK=$(cygpath -w $QPID_WORK) +fi + +#Set the default system properties that we'll use now that they have +#all been initialised +SYSTEM_PROPS="-Damqj.logging.level=$AMQJ_LOGGING_LEVEL -DQPID_HOME=$QPID_HOME -DQPID_WORK=$QPID_WORK" + +#If logprefix or logsuffix set to use PID make that happen +#Otherwise just pass the value through for these props +#Using X character to avoid probs with empty strings +if [ -n "$QPID_LOG_PREFIX" ]; then + if [ "X$QPID_LOG_PREFIX" = "XPID" ]; then + log $INFO Using pid in qpid log name prefix + LOG_PREFIX=" -Dlogprefix=$$" + else + log $INFO Using qpid logprefix property + LOG_PREFIX=" -Dlogprefix=$QPID_LOG_PREFIX" + fi + SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_PREFIX}" +fi + +if [ -n "$QPID_LOG_SUFFIX" ]; then + if [ "X$QPID_LOG_SUFFIX" = "XPID" ]; then + log $INFO Using pid in qpid log name suffix + LOG_SUFFIX=" -Dlogsuffix=$$" + else + log $INFO Using qpig logsuffix property + LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX" + fi + SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}" +fi + +log $INFO System Properties set to $SYSTEM_PROPS + +program=$(basename $0) +sourced=${BASH_SOURCE[0]} +if [[ -z ${sourced:-''} ]]; then + sourced=$(which qpid-run) || ${QPID_HOME}/bin/qpid-run +fi + +usage() { + echo Usage: $program ... "[-run: