From 5de8db83d669a00a8ac19023bbece140421316ab Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Mon, 19 Nov 2007 15:51:11 +0000 Subject: The eclipse License was missing from the NOTICE file git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/tags/M2@596339 13f79535-47bb-0310-9956-ffa450edef68 --- Final2/java/broker/bin/msTool.sh | 60 + Final2/java/broker/bin/qpid-passwd | 30 + Final2/java/broker/bin/qpid-server | 31 + Final2/java/broker/bin/qpid-server-bdb.bat | 22 + Final2/java/broker/bin/qpid-server.bat | 70 + Final2/java/broker/bin/qpid.start | 21 + Final2/java/broker/bin/qpid.stop | 137 + Final2/java/broker/bin/qpid.stopall | 74 + Final2/java/broker/bin/run.bat | 31 + Final2/java/broker/bin/run.sh | 44 + Final2/java/broker/bin/runAll | 37 + Final2/java/broker/distribution/pom.xml | 153 ++ .../src/main/assembly/broker-bin-tests.xml | 116 + .../distribution/src/main/assembly/broker-bin.xml | 183 ++ .../distribution/src/main/assembly/broker-src.xml | 78 + Final2/java/broker/etc/access | 19 + Final2/java/broker/etc/config.xml | 175 ++ Final2/java/broker/etc/debug.log4j.xml | 114 + Final2/java/broker/etc/jmxremote.access | 23 + Final2/java/broker/etc/log4j.xml | 112 + Final2/java/broker/etc/md5passwd | 21 + Final2/java/broker/etc/mstool-log4j.xml | 54 + Final2/java/broker/etc/passwd | 19 + Final2/java/broker/etc/passwdVhost | 19 + Final2/java/broker/etc/persistent_config.xml | 132 + Final2/java/broker/etc/qpid-server.conf | 25 + Final2/java/broker/etc/qpid-server.conf.jpp | 49 + Final2/java/broker/etc/qpid.passwd | 22 + Final2/java/broker/etc/transient_config.xml | 128 + Final2/java/broker/etc/virtualhosts.xml | 123 + Final2/java/broker/pom.xml | 256 ++ Final2/java/broker/python-test.xml | 56 + .../java/broker/src/main/grammar/SelectorParser.jj | 604 +++++ Final2/java/broker/src/main/java/log4j.properties | 24 + .../apache/log4j/QpidCompositeRollingAppender.java | 1007 +++++++ .../apache/qpid/configuration/Configuration.java | 188 ++ .../apache/qpid/server/AMQBrokerManagerMBean.java | 244 ++ .../java/org/apache/qpid/server/AMQChannel.java | 989 +++++++ .../qpid/server/ConsumerTagNotUniqueException.java | 25 + .../src/main/java/org/apache/qpid/server/Main.java | 521 ++++ .../org/apache/qpid/server/ManagedChannel.java | 68 + .../qpid/server/RequiredDeliveryException.java | 68 + .../java/org/apache/qpid/server/ack/TxAck.java | 139 + .../qpid/server/ack/UnacknowledgedMessage.java | 79 + .../qpid/server/ack/UnacknowledgedMessageMap.java | 80 + .../server/ack/UnacknowledgedMessageMapImpl.java | 235 ++ .../qpid/server/configuration/Configurator.java | 118 + .../configuration/VirtualHostConfiguration.java | 269 ++ .../qpid/server/exchange/AbstractExchange.java | 217 ++ .../server/exchange/DefaultExchangeFactory.java | 75 + .../server/exchange/DefaultExchangeRegistry.java | 138 + .../qpid/server/exchange/DestNameExchange.java | 229 ++ .../qpid/server/exchange/DestWildExchange.java | 426 +++ .../org/apache/qpid/server/exchange/Exchange.java | 97 + .../qpid/server/exchange/ExchangeFactory.java | 32 + .../server/exchange/ExchangeInUseException.java | 45 + .../qpid/server/exchange/ExchangeRegistry.java | 51 + .../qpid/server/exchange/FanoutExchange.java | 209 ++ .../qpid/server/exchange/HeadersBinding.java | 219 ++ .../qpid/server/exchange/HeadersExchange.java | 323 +++ .../org/apache/qpid/server/exchange/Index.java | 90 + .../qpid/server/exchange/ManagedExchange.java | 98 + .../apache/qpid/server/exchange/MessageRouter.java | 40 + .../qpid/server/exchange/NoRouteException.java | 48 + .../qpid/server/filter/ArithmeticExpression.java | 275 ++ .../qpid/server/filter/BinaryExpression.java | 106 + .../qpid/server/filter/BooleanExpression.java | 40 + .../qpid/server/filter/ComparisonExpression.java | 595 +++++ .../qpid/server/filter/ConstantExpression.java | 210 ++ .../org/apache/qpid/server/filter/Expression.java | 37 + .../apache/qpid/server/filter/FilterManager.java | 37 + .../qpid/server/filter/FilterManagerFactory.java | 77 + .../qpid/server/filter/JMSSelectorFilter.java | 68 + .../apache/qpid/server/filter/LogicExpression.java | 110 + .../apache/qpid/server/filter/MessageFilter.java | 29 + .../qpid/server/filter/NoConsumerFilter.java | 42 + .../qpid/server/filter/PropertyExpression.java | 322 +++ .../qpid/server/filter/SimpleFilterManager.java | 76 + .../apache/qpid/server/filter/UnaryExpression.java | 337 +++ .../apache/qpid/server/filter/XPathExpression.java | 126 + .../qpid/server/filter/XQueryExpression.java | 57 + .../qpid/server/filter/XalanXPathEvaluator.java | 102 + .../qpid/server/handler/BasicAckMethodHandler.java | 67 + .../server/handler/BasicCancelMethodHandler.java | 79 + .../server/handler/BasicConsumeMethodHandler.java | 173 ++ .../qpid/server/handler/BasicGetMethodHandler.java | 95 + .../server/handler/BasicPublishMethodHandler.java | 105 + .../qpid/server/handler/BasicQosHandler.java | 58 + .../server/handler/BasicRecoverMethodHandler.java | 64 + .../server/handler/BasicRejectMethodHandler.java | 130 + .../qpid/server/handler/ChannelCloseHandler.java | 78 + .../qpid/server/handler/ChannelCloseOkHandler.java | 53 + .../qpid/server/handler/ChannelFlowHandler.java | 72 + .../qpid/server/handler/ChannelOpenHandler.java | 61 + .../handler/ConnectionCloseMethodHandler.java | 71 + .../handler/ConnectionCloseOkMethodHandler.java | 63 + .../handler/ConnectionOpenMethodHandler.java | 118 + .../handler/ConnectionSecureOkMethodHandler.java | 141 + .../handler/ConnectionStartOkMethodHandler.java | 173 ++ .../handler/ConnectionTuneOkMethodHandler.java | 54 + .../qpid/server/handler/ExchangeBoundHandler.java | 205 ++ .../server/handler/ExchangeDeclareHandler.java | 114 + .../qpid/server/handler/ExchangeDeleteHandler.java | 70 + .../server/handler/OnCurrentThreadExecutor.java | 34 + .../qpid/server/handler/QueueBindHandler.java | 135 + .../qpid/server/handler/QueueDeclareHandler.java | 206 ++ .../qpid/server/handler/QueueDeleteHandler.java | 122 + .../qpid/server/handler/QueuePurgeHandler.java | 115 + .../qpid/server/handler/TxCommitHandler.java | 77 + .../qpid/server/handler/TxRollbackHandler.java | 73 + .../qpid/server/handler/TxSelectHandler.java | 63 + .../org/apache/qpid/server/jms/JmsConsumer.java | 110 + .../qpid/server/management/AMQManagedObject.java | 97 + .../server/management/DefaultManagedObject.java | 191 ++ .../management/JMXManagedObjectRegistry.java | 283 ++ .../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 | 62 + .../amqp0_8/ProtocolOutputConverterImpl.java | 282 ++ .../server/protocol/AMQMinaProtocolSession.java | 773 ++++++ .../protocol/AMQNoMethodHandlerException.java | 46 + .../server/protocol/AMQPFastProtocolHandler.java | 240 ++ .../qpid/server/protocol/AMQPProtocolProvider.java | 52 + .../qpid/server/protocol/AMQProtocolSession.java | 175 ++ .../server/protocol/AMQProtocolSessionMBean.java | 305 +++ .../qpid/server/protocol/ExchangeInitialiser.java | 49 + .../qpid/server/protocol/HeartbeatConfig.java | 67 + .../qpid/server/protocol/ManagedConnection.java | 135 + .../protocol/UnknnownMessageTypeException.java | 46 + .../org/apache/qpid/server/queue/AMQMessage.java | 1000 +++++++ .../apache/qpid/server/queue/AMQMessageHandle.java | 79 + .../org/apache/qpid/server/queue/AMQQueue.java | 940 +++++++ .../apache/qpid/server/queue/AMQQueueMBean.java | 471 ++++ .../qpid/server/queue/AsyncDeliveryConfig.java | 56 + .../queue/ConcurrentSelectorDeliveryManager.java | 1049 ++++++++ .../qpid/server/queue/DefaultQueueRegistry.java | 71 + .../apache/qpid/server/queue/DeliveryManager.java | 100 + .../apache/qpid/server/queue/ExchangeBindings.java | 137 + .../qpid/server/queue/FailedDequeueException.java | 50 + .../qpid/server/queue/InMemoryMessageHandle.java | 143 + .../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 + .../server/queue/QueueNotificationListener.java | 27 + .../apache/qpid/server/queue/QueueRegistry.java | 43 + .../org/apache/qpid/server/queue/Subscription.java | 63 + .../qpid/server/queue/SubscriptionFactory.java | 43 + .../apache/qpid/server/queue/SubscriptionImpl.java | 680 +++++ .../qpid/server/queue/SubscriptionManager.java | 34 + .../apache/qpid/server/queue/SubscriptionSet.java | 229 ++ .../qpid/server/queue/TransientMessageData.java | 121 + .../server/queue/WeakReferenceMessageHandle.java | 227 ++ .../server/queue/WeightedSubscriptionManager.java | 26 + .../qpid/server/registry/ApplicationRegistry.java | 213 ++ .../ConfigurationFileApplicationRegistry.java | 176 ++ .../qpid/server/registry/IApplicationRegistry.java | 71 + .../security/access/AMQUserManagementMBean.java | 467 ++++ .../qpid/server/security/access/AccessManager.java | 34 + .../server/security/access/AccessManagerImpl.java | 155 ++ .../qpid/server/security/access/AccessResult.java | 66 + .../qpid/server/security/access/AccessRights.java | 63 + .../qpid/server/security/access/Accessable.java | 27 + .../qpid/server/security/access/AllowAll.java | 42 + .../qpid/server/security/access/DenyAll.java | 41 + .../server/security/access/FileAccessManager.java | 183 ++ .../access/PrincipalDatabaseAccessManager.java | 108 + .../server/security/access/UserManagement.java | 118 + .../server/security/access/VirtualHostAccess.java | 68 + .../server/security/auth/AuthenticationResult.java | 43 + .../Base64MD5PasswordFilePrincipalDatabase.java | 599 +++++ .../ConfigurationFilePrincipalDatabaseManager.java | 236 ++ .../PlainPasswordFilePrincipalDatabase.java | 240 ++ .../PlainPasswordVhostFilePrincipalDatabase.java | 130 + .../security/auth/database/PrincipalDatabase.java | 100 + .../auth/database/PrincipalDatabaseManager.java | 34 + .../auth/database/PropertiesPrincipalDatabase.java | 160 ++ .../PropertiesPrincipalDatabaseManager.java | 48 + .../auth/manager/AuthenticationManager.java | 37 + .../PrincipalDatabaseAuthenticationManager.java | 249 ++ .../sasl/AuthenticationProviderInitialiser.java | 76 + .../server/security/auth/sasl/JCAProvider.java | 47 + .../auth/sasl/UsernamePasswordInitialiser.java | 123 + .../security/auth/sasl/UsernamePrincipal.java | 44 + .../auth/sasl/amqplain/AmqPlainInitialiser.java | 38 + .../auth/sasl/amqplain/AmqPlainSaslServer.java | 129 + .../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 | 149 ++ .../auth/sasl/plain/PlainSaslServerFactory.java | 60 + .../org/apache/qpid/server/state/AMQState.java | 36 + .../apache/qpid/server/state/AMQStateManager.java | 284 ++ .../state/IllegalStateTransitionException.java | 52 + .../server/state/StateAwareMethodListener.java | 35 + .../apache/qpid/server/state/StateListener.java | 30 + .../qpid/server/store/MemoryMessageStore.java | 209 ++ .../org/apache/qpid/server/store/MessageStore.java | 261 ++ .../org/apache/qpid/server/store/StoreContext.java | 68 + .../server/transport/ConnectorConfiguration.java | 97 + .../qpid/server/transport/ThreadPoolFilter.java | 705 +++++ .../qpid/server/txn/CleanupMessageOperation.java | 77 + .../qpid/server/txn/LocalTransactionalContext.java | 262 ++ .../qpid/server/txn/NonTransactionalContext.java | 227 ++ .../qpid/server/txn/StoreMessageOperation.java | 58 + .../qpid/server/txn/TransactionalContext.java | 171 ++ .../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 | 122 + .../server/virtualhost/ManagedVirtualHost.java | 44 + .../qpid/server/virtualhost/VirtualHost.java | 259 ++ .../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 | 299 +++ .../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 | 205 ++ .../qpid/tools/messagestore/commands/Purge.java | 68 + .../qpid/tools/messagestore/commands/Quit.java | 54 + .../qpid/tools/messagestore/commands/Select.java | 233 ++ .../qpid/tools/messagestore/commands/Show.java | 513 ++++ .../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 +++ .../apache/qpid/server/RunBrokerWithCommand.java | 130 + .../server/configuration/TestPropertyUtils.java | 50 + .../qpid/server/exchange/DestWildExchangeTest.java | 607 +++++ .../qpid/server/exchange/ExchangeMBeanTest.java | 138 + .../qpid/server/exchange/HeadersBindingTest.java | 199 ++ .../apache/qpid/server/protocol/TestIoSession.java | 295 +++ .../server/protocol/TestMinaProtocolSession.java | 52 + .../qpid/server/queue/AMQQueueAlertTest.java | 301 +++ .../qpid/server/queue/AMQQueueMBeanTest.java | 300 +++ .../server/store/TestableMemoryMessageStore.java | 73 + .../apache/qpid/server/util/LoggingProxyTest.java | 88 + Final2/java/client-java14/README.txt | 33 + Final2/java/client-java14/etc/sasl.properties | 20 + Final2/java/client-java14/pom.xml | 224 ++ .../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 + Final2/java/client/distribution/pom.xml | 156 ++ .../src/main/assembly/client-bin-tests.xml | 107 + .../distribution/src/main/assembly/client-bin.xml | 78 + .../src/main/assembly/client-java1.4-bin.xml | 74 + .../distribution/src/main/assembly/client-src.xml | 62 + Final2/java/client/example/bin/set_classpath.bat | 50 + Final2/java/client/example/bin/set_classpath.sh | 83 + Final2/java/client/example/pom.xml | 152 ++ Final2/java/client/example/source-jar.xml | 35 + .../main/java/org/apache/qpid/example/log4j.xml | 45 + .../example/publisher/FileMessageDispatcher.java | 162 ++ .../qpid/example/publisher/FileMessageFactory.java | 136 + .../example/publisher/MessageFactoryException.java | 54 + .../publisher/MonitorMessageDispatcher.java | 141 + .../qpid/example/publisher/MonitorPublisher.java | 104 + .../apache/qpid/example/publisher/Publisher.java | 181 ++ .../publisher/UndeliveredMessageException.java | 57 + .../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 | 54 + .../qpid/example/shared/ContextException.java | 54 + .../org/apache/qpid/example/shared/FileUtils.java | 168 ++ .../qpid/example/shared/InitialContextHelper.java | 80 + .../org/apache/qpid/example/shared/Statics.java | 57 + .../apache/qpid/example/shared/example.properties | 39 + .../example/subscriber/MonitoredSubscriber.java | 139 + .../subscriber/MonitoredSubscriptionWrapper.java | 51 + .../apache/qpid/example/subscriber/Subscriber.java | 181 ++ .../example/subscriber/SubscriptionWrapper.java | 51 + Final2/java/client/pom.xml | 249 ++ Final2/java/client/src/main/java/client.log4j | 28 + .../qpid/client/AMQAuthenticationException.java | 42 + .../org/apache/qpid/client/AMQBrokerDetails.java | 353 +++ .../java/org/apache/qpid/client/AMQConnection.java | 1289 +++++++++ .../apache/qpid/client/AMQConnectionFactory.java | 409 +++ .../org/apache/qpid/client/AMQConnectionURL.java | 455 ++++ .../org/apache/qpid/client/AMQDestination.java | 451 ++++ .../org/apache/qpid/client/AMQHeadersExchange.java | 59 + .../qpid/client/AMQNoConsumersException.java | 40 + .../apache/qpid/client/AMQNoRouteException.java | 40 + .../main/java/org/apache/qpid/client/AMQQueue.java | 149 ++ .../org/apache/qpid/client/AMQQueueBrowser.java | 136 + .../apache/qpid/client/AMQQueueSessionAdaptor.java | 204 ++ .../java/org/apache/qpid/client/AMQSession.java | 2800 ++++++++++++++++++++ .../org/apache/qpid/client/AMQSessionAdapter.java | 26 + .../org/apache/qpid/client/AMQTemporaryQueue.java | 69 + .../org/apache/qpid/client/AMQTemporaryTopic.java | 72 + .../main/java/org/apache/qpid/client/AMQTopic.java | 115 + .../apache/qpid/client/AMQTopicSessionAdaptor.java | 226 ++ .../qpid/client/AMQUndefinedDestination.java | 45 + .../apache/qpid/client/BasicMessageConsumer.java | 996 +++++++ .../apache/qpid/client/BasicMessageProducer.java | 691 +++++ .../java/org/apache/qpid/client/Closeable.java | 83 + .../qpid/client/ConnectionTuneParameters.java | 72 + .../org/apache/qpid/client/CustomJMSXProperty.java | 65 + .../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 | 230 ++ .../org/apache/qpid/client/SSLConfiguration.java | 61 + .../apache/qpid/client/TemporaryDestination.java | 38 + .../apache/qpid/client/TopicPublisherAdapter.java | 205 ++ .../apache/qpid/client/TopicSubscriberAdaptor.java | 126 + .../qpid/client/failover/FailoverException.java | 49 + .../qpid/client/failover/FailoverHandler.java | 240 ++ .../qpid/client/failover/FailoverNoopSupport.java | 75 + .../failover/FailoverProtectedOperation.java | 49 + .../qpid/client/failover/FailoverRetrySupport.java | 128 + .../apache/qpid/client/failover/FailoverState.java | 64 + .../qpid/client/failover/FailoverSupport.java | 47 + .../client/handler/BasicCancelOkMethodHandler.java | 59 + .../client/handler/BasicDeliverMethodHandler.java | 52 + .../client/handler/BasicReturnMethodHandler.java | 53 + .../client/handler/ChannelCloseMethodHandler.java | 105 + .../handler/ChannelCloseOkMethodHandler.java | 50 + .../client/handler/ChannelFlowOkMethodHandler.java | 52 + .../handler/ConnectionCloseMethodHandler.java | 102 + .../handler/ConnectionOpenOkMethodHandler.java | 48 + .../handler/ConnectionRedirectMethodHandler.java | 73 + .../handler/ConnectionSecureMethodHandler.java | 73 + .../handler/ConnectionStartMethodHandler.java | 239 ++ .../handler/ConnectionTuneMethodHandler.java | 97 + .../handler/ExchangeBoundOkMethodHandler.java | 59 + .../client/handler/QueueDeleteOkMethodHandler.java | 58 + .../org/apache/qpid/client/message/AMQMessage.java | 135 + .../qpid/client/message/AbstractBytesMessage.java | 151 ++ .../client/message/AbstractBytesTypedMessage.java | 801 ++++++ .../qpid/client/message/AbstractJMSMessage.java | 685 +++++ .../client/message/AbstractJMSMessageFactory.java | 103 + .../qpid/client/message/JMSBytesMessage.java | 388 +++ .../client/message/JMSBytesMessageFactory.java | 43 + .../qpid/client/message/JMSHeaderAdapter.java | 552 ++++ .../apache/qpid/client/message/JMSMapMessage.java | 507 ++++ .../qpid/client/message/JMSMapMessageFactory.java | 43 + .../qpid/client/message/JMSObjectMessage.java | 197 ++ .../client/message/JMSObjectMessageFactory.java | 43 + .../qpid/client/message/JMSStreamMessage.java | 204 ++ .../client/message/JMSStreamMessageFactory.java | 43 + .../apache/qpid/client/message/JMSTextMessage.java | 201 ++ .../qpid/client/message/JMSTextMessageFactory.java | 46 + .../qpid/client/message/MessageConverter.java | 202 ++ .../apache/qpid/client/message/MessageFactory.java | 41 + .../client/message/MessageFactoryRegistry.java | 127 + .../message/UnexpectedBodyReceivedException.java | 45 + .../qpid/client/message/UnprocessedMessage.java | 131 + .../qpid/client/protocol/AMQProtocolHandler.java | 732 +++++ .../qpid/client/protocol/AMQProtocolSession.java | 459 ++++ .../protocol/BlockingMethodFrameListener.java | 311 +++ .../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 | 21 + .../qpid/client/security/DynamicSaslRegistrar.java | 198 ++ .../security/DynamicSaslRegistrar.properties | 20 + .../apache/qpid/client/security/JCAProvider.java | 71 + .../UsernameHashedPasswordCallbackHandler.java | 102 + .../security/UsernamePasswordCallbackHandler.java | 60 + .../security/amqplain/AmqPlainSaslClient.java | 105 + .../amqplain/AmqPlainSaslClientFactory.java | 63 + .../CRAMMD5HashedSaslClientFactory.java | 72 + .../org/apache/qpid/client/state/AMQState.java | 56 + .../qpid/client/state/AMQStateChangedEvent.java | 48 + .../apache/qpid/client/state/AMQStateListener.java | 26 + .../apache/qpid/client/state/AMQStateManager.java | 276 ++ .../state/IllegalStateTransitionException.java | 53 + .../client/state/StateAwareMethodListener.java | 36 + .../apache/qpid/client/state/StateListener.java | 30 + .../org/apache/qpid/client/state/StateWaiter.java | 122 + .../listener/SpecificMethodFrameListener.java | 57 + .../AMQNoTransportForProtocolException.java | 62 + .../transport/AMQTransportConnectionException.java | 41 + .../client/transport/ITransportConnection.java | 32 + .../transport/SocketTransportConnection.java | 101 + .../qpid/client/transport/TransportConnection.java | 318 +++ .../transport/VmPipeTransportConnection.java | 65 + .../client/util/FlowControllingBlockingQueue.java | 109 + .../vmbroker/AMQVMBrokerCreationException.java | 58 + .../java/org/apache/qpid/jms/BrokerDetails.java | 74 + .../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 | 86 + .../java/org/apache/qpid/jms/FailoverPolicy.java | 324 +++ .../src/main/java/org/apache/qpid/jms/Message.java | 28 + .../java/org/apache/qpid/jms/MessageConsumer.java | 27 + .../java/org/apache/qpid/jms/MessageProducer.java | 53 + .../src/main/java/org/apache/qpid/jms/Session.java | 101 + .../apache/qpid/jms/failover/FailoverMethod.java | 76 + .../jms/failover/FailoverRoundRobinServers.java | 261 ++ .../qpid/jms/failover/FailoverSingleServer.java | 147 + .../java/org/apache/qpid/jndi/Example.properties | 39 + .../java/org/apache/qpid/jndi/NameParserImpl.java | 37 + .../jndi/PropertiesFileInitialContextFactory.java | 337 +++ .../java/org/apache/qpid/jndi/ReadOnlyContext.java | 527 ++++ .../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 + .../qpid/client/AMQQueueDeferredOrderingTest.java | 152 ++ .../org/apache/qpid/client/DispatcherTest.java | 252 ++ ...sageListenerMultiConsumerImmediatePrefetch.java | 44 + .../client/MessageListenerMultiConsumerTest.java | 252 ++ .../apache/qpid/client/MessageListenerTest.java | 195 ++ .../qpid/client/ResetMessageListenerTest.java | 277 ++ .../client/SpecificMethodFrameListenerTest.java | 71 + .../qpid/client/message/NonQpidObjectMessage.java | 234 ++ .../qpid/client/message/TestMessageHelper.java | 46 + .../org/apache/qpid/test/unit/ack/RecoverTest.java | 335 +++ .../qpid/test/unit/basic/BytesMessageTest.java | 288 ++ .../unit/basic/FieldTableKeyEnumeratorTest.java | 96 + .../test/unit/basic/FieldTableMessageTest.java | 176 ++ .../test/unit/basic/FieldTablePropertyTest.java | 62 + .../test/unit/basic/InvalidDestinationTest.java | 130 + .../qpid/test/unit/basic/LargeMessageTest.java | 194 ++ .../qpid/test/unit/basic/MapMessageTest.java | 1277 +++++++++ .../test/unit/basic/MultipleConnectionTest.java | 231 ++ .../qpid/test/unit/basic/ObjectMessageTest.java | 276 ++ .../qpid/test/unit/basic/PropertyValueTest.java | 369 +++ .../test/unit/basic/PubSubTwoConnectionTest.java | 77 + .../apache/qpid/test/unit/basic/ReceiveTest.java | 115 + .../apache/qpid/test/unit/basic/SelectorTest.java | 140 + .../qpid/test/unit/basic/SessionStartTest.java | 122 + .../qpid/test/unit/basic/TextMessageTest.java | 257 ++ .../qpid/test/unit/basic/close/CloseTests.java | 80 + .../qpid/test/unit/client/AMQConnectionTest.java | 205 ++ .../qpid/test/unit/client/AMQSessionTest.java | 115 + .../client/BrokerDetails/BrokerDetailsTest.java | 94 + .../ChannelCloseMethodHandlerNoCloseOk.java | 100 + .../client/channelclose/ChannelCloseOkTest.java | 235 ++ .../unit/client/channelclose/ChannelCloseTest.java | 412 +++ .../channelclose/CloseWithBlockingReceiveTest.java | 87 + .../client/channelclose/NoCloseOKStateManager.java | 107 + .../client/connection/ConnectionStartTest.java | 175 ++ .../unit/client/connection/ConnectionTest.java | 194 ++ .../client/connectionurl/ConnectionURLTest.java | 481 ++++ .../client/destinationurl/DestinationURLTest.java | 145 + .../qpid/test/unit/client/forwardall/Client.java | 123 + .../test/unit/client/forwardall/CombinedTest.java | 68 + .../qpid/test/unit/client/forwardall/Service.java | 84 + .../unit/client/forwardall/ServiceCreator.java | 112 + .../test/unit/client/forwardall/SpecialQueue.java | 46 + .../test/unit/client/message/BytesMessageTest.java | 569 ++++ .../test/unit/client/message/MapMessageTest.java | 383 +++ .../unit/client/message/ObjectMessageTest.java | 345 +++ .../unit/client/message/StreamMessageTest.java | 623 +++++ .../test/unit/client/message/TextMessageTest.java | 300 +++ .../test/unit/client/protocol/TestIoSession.java | 104 + .../client/temporaryqueue/TemporaryQueueTest.java | 232 ++ .../qpid/test/unit/close/CloseBeforeAckTest.java | 147 + .../qpid/test/unit/close/MessageRequeueTest.java | 389 +++ .../test/unit/close/TopicPublisherCloseTest.java | 72 + .../qpid/test/unit/message/JMSDestinationTest.java | 94 + .../qpid/test/unit/message/JMSPropertiesTest.java | 108 + .../test/unit/message/MessageConverterTest.java | 138 + .../qpid/test/unit/message/NonQpidMessage.java | 411 +++ .../qpid/test/unit/message/StreamMessageTest.java | 160 ++ .../test/unit/topic/DurableSubscriptionTest.java | 192 ++ .../qpid/test/unit/topic/TopicPublisherTest.java | 80 + .../qpid/test/unit/topic/TopicSessionTest.java | 375 +++ .../test/unit/transacted/CommitRollbackTest.java | 532 ++++ .../qpid/test/unit/transacted/TransactedTest.java | 313 +++ .../test/java/org/apache/qpid/testutil/Config.java | 199 ++ .../apache/qpid/testutil/QpidClientConnection.java | 287 ++ .../org/apache/qpid/testutil/VMBrokerSetup.java | 52 + Final2/java/client/test/bin/IBM-JNDI-Setup.bat | 69 + Final2/java/client/test/bin/IBM-JNDI-Setup.sh | 27 + Final2/java/client/test/bin/IBM-Publisher.bat | 62 + Final2/java/client/test/bin/IBM-Publisher.sh | 22 + Final2/java/client/test/bin/IBM-PutGet.bat | 62 + Final2/java/client/test/bin/IBM-PutGet.sh | 21 + Final2/java/client/test/bin/IBM-README.txt | 19 + Final2/java/client/test/bin/IBM-Receiver.bat | 62 + Final2/java/client/test/bin/IBM-Receiver.sh | 22 + Final2/java/client/test/bin/IBM-Sender.bat | 62 + Final2/java/client/test/bin/IBM-Sender.sh | 22 + Final2/java/client/test/bin/IBM-Subscriber.bat | 62 + Final2/java/client/test/bin/IBM-Subscriber.sh | 22 + Final2/java/client/test/bin/headersListener.sh | 22 + .../java/client/test/bin/headersListenerGroup.sh | 25 + Final2/java/client/test/bin/headersPublisher.sh | 22 + Final2/java/client/test/bin/run_many.sh | 30 + .../java/client/test/bin/serviceProvidingClient.sh | 24 + .../client/test/bin/serviceRequestingClient.sh | 27 + Final2/java/client/test/bin/testService.sh | 22 + Final2/java/client/test/bin/topicListener.sh | 23 + Final2/java/client/test/bin/topicPublisher.sh | 22 + Final2/java/client/test/etc/ApacheDS.properties | 24 + Final2/java/client/test/example_build.xml | 104 + Final2/java/cluster/doc/design.doc | Bin 0 -> 70144 bytes Final2/java/cluster/pom.xml | 69 + .../server/cluster/AMQConnectionWaitException.java | 42 + .../cluster/AMQUnexpectedBodyTypeException.java | 46 + .../cluster/AMQUnexpectedFrameTypeException.java | 45 + .../qpid/server/cluster/BlockingHandler.java | 91 + .../qpid/server/cluster/BroadcastPolicy.java | 26 + .../org/apache/qpid/server/cluster/Broker.java | 247 ++ .../apache/qpid/server/cluster/BrokerFactory.java | 26 + .../apache/qpid/server/cluster/BrokerGroup.java | 368 +++ .../apache/qpid/server/cluster/ClientAdapter.java | 73 + .../qpid/server/cluster/ClientHandlerRegistry.java | 130 + .../apache/qpid/server/cluster/ClusterBuilder.java | 63 + .../qpid/server/cluster/ClusterCapability.java | 60 + .../server/cluster/ClusteredProtocolHandler.java | 190 ++ .../server/cluster/ClusteredProtocolSession.java | 133 + .../server/cluster/ConnectionStatusMonitor.java | 80 + .../qpid/server/cluster/DefaultGroupManager.java | 396 +++ .../apache/qpid/server/cluster/GroupManager.java | 72 + .../apache/qpid/server/cluster/GroupRequest.java | 107 + .../qpid/server/cluster/GroupResponseHandler.java | 31 + .../qpid/server/cluster/InductionBuffer.java | 90 + .../org/apache/qpid/server/cluster/JoinState.java | 26 + .../org/apache/qpid/server/cluster/LoadTable.java | 107 + .../java/org/apache/qpid/server/cluster/Main.java | 117 + .../org/apache/qpid/server/cluster/Member.java | 31 + .../qpid/server/cluster/MemberFailureListener.java | 26 + .../apache/qpid/server/cluster/MemberHandle.java | 36 + .../server/cluster/MembershipChangeListener.java | 28 + .../apache/qpid/server/cluster/MethodHandler.java | 29 + .../qpid/server/cluster/MethodHandlerFactory.java | 28 + .../qpid/server/cluster/MethodHandlerRegistry.java | 44 + .../qpid/server/cluster/MinaBrokerProxy.java | 272 ++ .../server/cluster/MinaBrokerProxyFactory.java | 36 + .../qpid/server/cluster/ResponseHandler.java | 30 + .../org/apache/qpid/server/cluster/Sendable.java | 28 + .../qpid/server/cluster/ServerHandlerRegistry.java | 98 + .../qpid/server/cluster/SimpleBodySendable.java | 48 + .../qpid/server/cluster/SimpleMemberHandle.java | 166 ++ .../apache/qpid/server/cluster/SimpleSendable.java | 55 + .../handler/ChainedClusterMethodHandler.java | 73 + .../cluster/handler/ChannelQueueManager.java | 136 + .../cluster/handler/ClusterMethodHandler.java | 51 + .../handler/ClusterMethodHandlerFactory.java | 239 ++ .../server/cluster/handler/ExtendedHandler.java | 55 + .../qpid/server/cluster/handler/HandlerUtils.java | 25 + .../cluster/handler/LocalQueueDeclareHandler.java | 79 + .../qpid/server/cluster/handler/NullListener.java | 38 + .../qpid/server/cluster/handler/PeerHandler.java | 60 + .../server/cluster/handler/QueueNameGenerator.java | 62 + .../cluster/handler/RemoteCancelHandler.java | 59 + .../cluster/handler/RemoteConsumeHandler.java | 69 + .../cluster/handler/ReplicatingConsumeHandler.java | 90 + .../server/cluster/handler/ReplicatingHandler.java | 125 + .../server/cluster/handler/WrappedListener.java | 56 + .../handler/WrappingMethodHandlerFactory.java | 85 + .../cluster/policy/AsynchBroadcastPolicy.java | 31 + .../policy/MajorityResponseBroadcastPolicy.java | 31 + .../cluster/policy/OneResponseBroadcastPolicy.java | 31 + .../server/cluster/policy/StandardPolicies.java | 29 + .../cluster/policy/SynchBroadcastPolicy.java | 31 + .../cluster/replay/ChainedMethodRecorder.java | 48 + .../qpid/server/cluster/replay/ConsumerCounts.java | 83 + .../qpid/server/cluster/replay/MethodRecorder.java | 32 + .../replay/RecordingMethodHandlerFactory.java | 77 + .../qpid/server/cluster/replay/ReplayManager.java | 37 + .../qpid/server/cluster/replay/ReplayStore.java | 311 +++ .../apache/qpid/server/cluster/util/Bindings.java | 83 + .../qpid/server/cluster/util/InvokeMultiple.java | 72 + .../qpid/server/cluster/util/LogMessage.java | 53 + .../qpid/server/cluster/util/MultiValuedMap.java | 61 + .../apache/qpid/server/queue/ClusteredQueue.java | 175 ++ .../server/queue/ClusteredSubscriptionManager.java | 102 + .../server/queue/NestedSubscriptionManager.java | 116 + .../org/apache/qpid/server/queue/PrivateQueue.java | 64 + .../qpid/server/queue/ProxiedQueueCleanup.java | 60 + .../apache/qpid/server/queue/RemoteQueueProxy.java | 91 + .../qpid/server/queue/RemoteSubscriptionImpl.java | 176 ++ .../qpid/server/queue/SubscriberCleanup.java | 56 + .../qpid/server/cluster/BrokerGroupTest.java | 270 ++ .../org/apache/qpid/server/cluster/BrokerTest.java | 237 ++ .../qpid/server/cluster/ClusterCapabilityTest.java | 43 + .../qpid/server/cluster/InductionBufferTest.java | 106 + .../qpid/server/cluster/RecordingBroker.java | 53 + .../server/cluster/RecordingBrokerFactory.java | 29 + .../qpid/server/cluster/SimpleClusterTest.java | 45 + .../server/cluster/SimpleMemberHandleTest.java | 57 + .../org/apache/qpid/server/cluster/TestBroker.java | 70 + .../qpid/server/cluster/TestBrokerFactory.java | 29 + .../qpid/server/cluster/TestReplayManager.java | 47 + .../apache/qpid/server/cluster/TestSession.java | 269 ++ Final2/java/common/bin/qpid-run | 238 ++ Final2/java/common/etc/qpid-run.conf | 25 + Final2/java/common/etc/qpid-run.conf.dev | 26 + Final2/java/common/pom.xml | 144 + Final2/java/common/protocol-version.xml | 60 + Final2/java/common/readme.txt | 4 + Final2/java/common/src/main/java/log4j.properties | 28 + .../org/apache/qpid/AMQChannelClosedException.java | 41 + .../java/org/apache/qpid/AMQChannelException.java | 68 + .../apache/qpid/AMQConnectionClosedException.java | 44 + .../org/apache/qpid/AMQConnectionException.java | 73 + .../apache/qpid/AMQConnectionFailureException.java | 40 + .../org/apache/qpid/AMQDisconnectedException.java | 39 + .../main/java/org/apache/qpid/AMQException.java | 101 + .../apache/qpid/AMQInvalidArgumentException.java | 39 + .../apache/qpid/AMQInvalidRoutingKeyException.java | 39 + .../org/apache/qpid/AMQPInvalidClassException.java | 39 + .../java/org/apache/qpid/AMQTimeoutException.java | 39 + .../org/apache/qpid/AMQUndeliveredException.java | 48 + .../org/apache/qpid/AMQUnknownExchangeType.java | 43 + .../apache/qpid/AMQUnresolvedAddressException.java | 50 + .../org/apache/qpid/codec/AMQCodecFactory.java | 77 + .../java/org/apache/qpid/codec/AMQDecoder.java | 163 ++ .../java/org/apache/qpid/codec/AMQEncoder.java | 66 + .../org/apache/qpid/common/AMQPFilterTypes.java | 61 + .../org/apache/qpid/common/ClientProperties.java | 37 + .../org/apache/qpid/common/QpidProperties.java | 190 ++ .../org/apache/qpid/configuration/Configured.java | 44 + .../qpid/configuration/PropertyException.java | 60 + .../apache/qpid/configuration/PropertyUtils.java | 164 ++ .../org/apache/qpid/exchange/ExchangeDefaults.java | 65 + .../main/java/org/apache/qpid/framing/AMQBody.java | 39 + .../java/org/apache/qpid/framing/AMQDataBlock.java | 53 + .../apache/qpid/framing/AMQDataBlockDecoder.java | 120 + .../apache/qpid/framing/AMQDataBlockEncoder.java | 61 + .../java/org/apache/qpid/framing/AMQFrame.java | 75 + .../qpid/framing/AMQFrameDecodingException.java | 41 + .../org/apache/qpid/framing/AMQMethodBody.java | 132 + .../apache/qpid/framing/AMQMethodBodyFactory.java | 46 + .../qpid/framing/AMQMethodBodyInstanceFactory.java | 31 + .../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 | 436 +++ .../main/java/org/apache/qpid/framing/AMQType.java | 701 +++++ .../java/org/apache/qpid/framing/AMQTypeMap.java | 48 + .../org/apache/qpid/framing/AMQTypedValue.java | 80 + .../qpid/framing/BasicContentHeaderProperties.java | 799 ++++++ .../java/org/apache/qpid/framing/BodyFactory.java | 31 + .../framing/CommonContentHeaderProperties.java | 81 + .../apache/qpid/framing/CompositeAMQDataBlock.java | 103 + .../main/java/org/apache/qpid/framing/Content.java | 26 + .../java/org/apache/qpid/framing/ContentBody.java | 106 + .../apache/qpid/framing/ContentBodyFactory.java | 48 + .../org/apache/qpid/framing/ContentHeaderBody.java | 123 + .../qpid/framing/ContentHeaderBodyFactory.java | 50 + .../qpid/framing/ContentHeaderProperties.java | 58 + .../framing/ContentHeaderPropertiesFactory.java | 57 + .../apache/qpid/framing/EncodableAMQDataBlock.java | 35 + .../org/apache/qpid/framing/EncodingUtils.java | 1028 +++++++ .../java/org/apache/qpid/framing/FieldTable.java | 1017 +++++++ .../org/apache/qpid/framing/FieldTableFactory.java | 38 + .../org/apache/qpid/framing/HeartbeatBody.java | 71 + .../apache/qpid/framing/HeartbeatBodyFactory.java | 31 + .../apache/qpid/framing/MethodConverter_8_0.java | 125 + .../apache/qpid/framing/ProtocolInitiation.java | 194 ++ .../qpid/framing/SmallCompositeAMQDataBlock.java | 98 + .../qpid/framing/VersionSpecificRegistry.java | 198 ++ .../abstraction/AbstractMethodConverter.java | 47 + .../qpid/framing/abstraction/ContentChunk.java | 32 + .../framing/abstraction/MessagePublishInfo.java | 36 + .../abstraction/MessagePublishInfoConverter.java | 32 + .../ProtocolVersionMethodConverter.java | 32 + .../src/main/java/org/apache/qpid/pool/Event.java | 155 ++ .../src/main/java/org/apache/qpid/pool/Job.java | 162 ++ .../java/org/apache/qpid/pool/PoolingFilter.java | 471 ++++ .../org/apache/qpid/pool/ReadWriteThreadModel.java | 102 + .../pool/ReferenceCountingExecutorService.java | 145 + .../java/org/apache/qpid/protocol/AMQConstant.java | 227 ++ .../org/apache/qpid/protocol/AMQMethodEvent.java | 95 + .../apache/qpid/protocol/AMQMethodListener.java | 69 + .../apache/qpid/protocol/AMQProtocolWriter.java | 43 + .../protocol/AMQVersionAwareProtocolSession.java | 46 + .../apache/qpid/protocol/ProtocolVersionAware.java | 47 + .../org/apache/qpid/ssl/SSLContextFactory.java | 157 ++ .../java/org/apache/qpid/url/AMQBindingURL.java | 294 ++ .../main/java/org/apache/qpid/url/BindingURL.java | 56 + .../main/java/org/apache/qpid/url/URLHelper.java | 172 ++ .../org/apache/qpid/url/URLSyntaxException.java | 97 + .../org/apache/qpid/util/CommandLineParser.java | 689 +++++ .../ConcurrentLinkedMessageQueueAtomicSize.java | 250 ++ .../qpid/util/ConcurrentLinkedQueueAtomicSize.java | 70 + .../qpid/util/ConcurrentLinkedQueueNoSize.java | 38 + .../main/java/org/apache/qpid/util/FileUtils.java | 195 ++ .../java/org/apache/qpid/util/MessageQueue.java | 43 + .../org/apache/qpid/util/PrettyPrintingUtils.java | 75 + .../java/org/apache/qpid/util/PropertiesUtils.java | 200 ++ .../java/org/apache/qpid/util/ReflectionUtils.java | 228 ++ .../apache/qpid/util/ReflectionUtilsException.java | 44 + .../main/resources/org/apache/qpid/ssl/qpid.cert | Bin 0 -> 756 bytes .../framing/BasicContentHeaderPropertiesTest.java | 188 ++ .../qpid/framing/PropertyFieldTableTest.java | 906 +++++++ .../org/apache/qpid/pool/PoolingFilterTest.java | 111 + .../java/org/apache/qpid/session/TestSession.java | 277 ++ .../apache/qpid/util/CommandLineParserTest.java | 554 ++++ Final2/java/distribution/pom.xml | 210 ++ .../distribution/src/main/assembly/bin-test.xml | 120 + Final2/java/distribution/src/main/assembly/bin.xml | 127 + .../assembly/management-eclipse-plugin-unix.xml | 131 + .../main/assembly/management-eclipse-plugin.xml | 162 ++ Final2/java/distribution/src/main/assembly/src.xml | 106 + Final2/java/doc/AMQBlazeDetailedDesign.vsd | Bin 0 -> 120320 bytes Final2/java/doc/FramingClassDiagram.vsd | Bin 0 -> 206848 bytes Final2/java/etc/coding_standards.xml | 118 + Final2/java/etc/license_header.txt | 20 + Final2/java/etc/log4j.xml | 61 + Final2/java/integrationtests/README.txt | 13 + .../docs/RunningSustainedTests.txt | 17 + .../integrationtests/jar-with-dependencies.xml | 47 + Final2/java/integrationtests/pom.xml | 146 + .../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 + .../framework/distributedtesting/TestClient.java | 463 ++++ .../src/resources/sustained-log4j.xml | 69 + .../management/eclipse-plugin/META-INF/MANIFEST.MF | 13 + Final2/java/management/eclipse-plugin/README.txt | 21 + .../java/management/eclipse-plugin/bin/qpidmc.bat | 55 + .../java/management/eclipse-plugin/bin/qpidmc.sh | 64 + .../management/eclipse-plugin/bin/qpidmc_gtk.sh | 24 + .../management/eclipse-plugin/bin/qpidmc_motif.sh | 24 + .../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 + Final2/java/management/eclipse-plugin/plugin.xml | 223 ++ Final2/java/management/eclipse-plugin/pom.xml | 252 ++ .../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 | 147 + .../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 | 136 + .../qpid/management/ui/actions/AddServer.java | 274 ++ .../management/ui/actions/CloseConnection.java | 56 + .../qpid/management/ui/actions/EditAttribute.java | 51 + .../management/ui/actions/ReconnectServer.java | 209 ++ .../apache/qpid/management/ui/actions/Refresh.java | 53 + .../qpid/management/ui/actions/RemoveServer.java | 50 + .../qpid/management/ui/actions/VersionAction.java | 89 + .../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 | 628 +++++ .../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 | 899 +++++++ .../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 | 641 +++++ .../src/main/resources/.eclipseproduct | 23 + .../eclipse-plugin/src/main/resources/eclipse.exe | Bin 0 -> 180224 bytes .../eclipse-plugin/src/main/resources/eclipse.ini | 4 + .../src/main/resources/license.eclipse.txt | 88 + .../src/main/resources/sasl/MANIFEST.MF | 8 + .../eclipse-plugin/src/main/resources/startup.jar | Bin 0 -> 33049 bytes .../main/resources/unix/configuration/config.ini | 27 + .../main/resources/win32/configuration/config.ini | 26 + .../qpid/management/ui/ManagementConsoleTest.java | 118 + Final2/java/perftests/RunningPerformanceTests.txt | 141 + Final2/java/perftests/bin/run_many.sh | 30 + Final2/java/perftests/bin/topicListener.sh | 33 + Final2/java/perftests/bin/topicPublisher.sh | 32 + Final2/java/perftests/dist-zip.xml | 45 + Final2/java/perftests/distribution/pom.xml | 131 + .../distribution/src/main/assembly/performance.xml | 103 + Final2/java/perftests/etc/scripts/CTQ-Qpid-1.sh | 21 + Final2/java/perftests/etc/scripts/CTQ-Qpid-2.sh | 21 + Final2/java/perftests/etc/scripts/CTQ-Qpid-3.sh | 21 + Final2/java/perftests/etc/scripts/CTQ-Qpid-4.sh | 21 + Final2/java/perftests/etc/scripts/CTQ-Qpid-5.sh | 21 + Final2/java/perftests/etc/scripts/CTQ-Qpid-6.sh | 21 + Final2/java/perftests/etc/scripts/PT-Qpid-13.sh | 42 + Final2/java/perftests/etc/scripts/PT-Qpid-14.sh | 41 + .../perftests/etc/scripts/sendAndWaitClient.sh | 22 + Final2/java/perftests/jar-with-dependencies.xml | 82 + Final2/java/perftests/pom.xml | 714 +++++ .../qpid/client/message/TestMessageFactory.java | 121 + .../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 ++ .../qpid/perftests/QpidTestThroughputPerf.java | 170 ++ .../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 | 314 +++ .../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 | 1688 ++++++++++++ .../apache/qpid/requestreply/PingPongTestPerf.java | 251 ++ .../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/perftests/src/main/java/perftests.log4j | 45 + Final2/java/pom.xml | 773 ++++++ Final2/java/release-docs/RELEASE_NOTES.txt | 160 ++ Final2/java/resources/DISCLAIMER | 5 + Final2/java/resources/LICENSE | 203 ++ Final2/java/resources/META-INF/DISCLAIMER | 10 + Final2/java/resources/META-INF/LICENSE | 203 ++ Final2/java/resources/META-INF/NOTICE | 105 + Final2/java/resources/NOTICE | 84 + Final2/java/resources/README | 40 + Final2/java/systests/distribution/pom.xml | 112 + .../distribution/src/main/assembly/systests.xml | 91 + Final2/java/systests/etc/bin/testclients.sh | 23 + Final2/java/systests/pom.xml | 111 + .../vmpipe/support/VmPipeIdleStatusChecker.java | 125 + .../qpid/server/AMQBrokerManagerMBeanTest.java | 92 + .../java/org/apache/qpid/server/ack/TxAckTest.java | 232 ++ .../exchange/AbstractHeadersExchangeTestBase.java | 336 +++ .../qpid/server/exchange/HeadersExchangeTest.java | 101 + .../qpid/server/exchange/ImmediateMessageTest.java | 296 +++ .../qpid/server/exchange/MandatoryMessageTest.java | 308 +++ .../ReturnUnroutableMandatoryMessageTest.java | 305 +++ .../apache/qpid/server/failure/DeadlockTest.java | 211 ++ .../apache/qpid/server/failure/HeapExhaustion.java | 230 ++ .../protocol/AMQProtocolSessionMBeanTest.java | 122 + .../qpid/server/protocol/MaxChannelsTest.java | 78 + .../apache/qpid/server/protocol/MockIoSession.java | 297 +++ .../java/org/apache/qpid/server/queue/AckTest.java | 374 +++ .../qpid/server/queue/ConcurrencyTestDisabled.java | 265 ++ .../qpid/server/queue/DeliveryManagerTest.java | 177 ++ .../qpid/server/queue/MessageReturnTest.java | 315 +++ .../qpid/server/queue/MessageTestHelper.java | 93 + .../qpid/server/queue/MockProtocolSession.java | 205 ++ .../qpid/server/queue/PersistentTestManual.java | 279 ++ .../server/queue/QueueDepthWithSelectorTest.java | 214 ++ .../qpid/server/queue/SubscriptionManagerTest.java | 102 + .../qpid/server/queue/SubscriptionSetTest.java | 144 + .../qpid/server/queue/SubscriptionTestHelper.java | 160 ++ .../apache/qpid/server/queue/TimeToLiveTest.java | 166 ++ .../qpid/server/store/SkeletonMessageStore.java | 147 + .../qpid/server/store/TestReferenceCounting.java | 146 + .../server/store/TestableMemoryMessageStore.java | 73 + .../org/apache/qpid/server/txn/TxnBufferTest.java | 307 +++ .../java/org/apache/qpid/server/txn/TxnTest.java | 188 ++ .../org/apache/qpid/server/util/AveragedRun.java | 66 + .../java/org/apache/qpid/server/util/RunStats.java | 57 + .../qpid/server/util/TestApplicationRegistry.java | 149 ++ .../java/org/apache/qpid/server/util/TimedRun.java | 52 + .../java/org/apache/qpid/test/VMBrokerSetup.java | 52 + .../main/java/org/apache/qpid/test/VMTestCase.java | 128 + .../apache/qpid/test/client/QueueBrowserTest.java | 152 ++ .../org/apache/qpid/test/framework/Assertion.java | 39 + .../apache/qpid/test/framework/AssertionBase.java | 66 + .../org/apache/qpid/test/framework/Circuit.java | 109 + .../org/apache/qpid/test/framework/CircuitEnd.java | 91 + .../apache/qpid/test/framework/CircuitEndBase.java | 149 ++ .../org/apache/qpid/test/framework/DropInTest.java | 51 + .../qpid/test/framework/ExceptionMonitor.java | 158 ++ .../qpid/test/framework/FrameworkBaseCase.java | 280 ++ .../test/framework/FrameworkClientBaseCase.java | 11 + .../apache/qpid/test/framework/MessageMonitor.java | 105 + .../framework/MessagingTestConfigProperties.java | 485 ++++ .../org/apache/qpid/test/framework/Publisher.java | 56 + .../org/apache/qpid/test/framework/Receiver.java | 48 + .../qpid/test/framework/TestClientDetails.java | 86 + .../org/apache/qpid/test/framework/TestUtils.java | 189 ++ .../clocksynch/ClockSynchFailureException.java | 45 + .../framework/clocksynch/ClockSynchThread.java | 123 + .../framework/clocksynch/ClockSynchronizer.java | 69 + .../clocksynch/LocalClockSynchronizer.java | 73 + .../framework/clocksynch/UDPClockReference.java | 166 ++ .../framework/clocksynch/UDPClockSynchronizer.java | 464 ++++ .../distributedcircuit/DistributedCircuitImpl.java | 469 ++++ .../DistributedPublisherImpl.java | 63 + .../DistributedReceiverImpl.java | 53 + .../distributedcircuit/TestClientCircuitEnd.java | 315 +++ .../framework/distributedtesting/Coordinator.java | 532 ++++ .../DistributedTestDecorator.java | 166 ++ .../distributedtesting/FanOutTestDecorator.java | 245 ++ .../distributedtesting/InteropTestDecorator.java | 208 ++ .../distributedtesting/OptOutTestCase.java | 69 + .../TestClientControlledTest.java | 108 + .../test/framework/listeners/XMLTestListener.java | 399 +++ .../framework/localcircuit/LocalCircuitImpl.java | 452 ++++ .../framework/localcircuit/LocalPublisherImpl.java | 180 ++ .../framework/localcircuit/LocalReceiverImpl.java | 100 + .../org/apache/qpid/test/framework/package.html | 22 + .../framework/sequencers/BaseCircuitFactory.java | 128 + .../test/framework/sequencers/CircuitFactory.java | 111 + .../framework/sequencers/FanOutCircuitFactory.java | 201 ++ .../sequencers/InteropCircuitFactory.java | 145 + .../qpid/testutil/QpidClientConnectionHelper.java | 296 +++ .../org/apache/qpid/util/ClasspathScanner.java | 234 ++ .../org/apache/qpid/util/ConversationFactory.java | 479 ++++ Final2/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 ++ 1029 files changed, 153293 insertions(+) create mode 100755 Final2/java/broker/bin/msTool.sh create mode 100644 Final2/java/broker/bin/qpid-passwd create mode 100644 Final2/java/broker/bin/qpid-server create mode 100644 Final2/java/broker/bin/qpid-server-bdb.bat create mode 100644 Final2/java/broker/bin/qpid-server.bat create mode 100644 Final2/java/broker/bin/qpid.start create mode 100644 Final2/java/broker/bin/qpid.stop create mode 100644 Final2/java/broker/bin/qpid.stopall create mode 100755 Final2/java/broker/bin/run.bat create mode 100755 Final2/java/broker/bin/run.sh create mode 100644 Final2/java/broker/bin/runAll create mode 100644 Final2/java/broker/distribution/pom.xml create mode 100644 Final2/java/broker/distribution/src/main/assembly/broker-bin-tests.xml create mode 100644 Final2/java/broker/distribution/src/main/assembly/broker-bin.xml create mode 100644 Final2/java/broker/distribution/src/main/assembly/broker-src.xml create mode 100644 Final2/java/broker/etc/access create mode 100644 Final2/java/broker/etc/config.xml create mode 100644 Final2/java/broker/etc/debug.log4j.xml create mode 100644 Final2/java/broker/etc/jmxremote.access create mode 100644 Final2/java/broker/etc/log4j.xml create mode 100644 Final2/java/broker/etc/md5passwd create mode 100644 Final2/java/broker/etc/mstool-log4j.xml create mode 100644 Final2/java/broker/etc/passwd create mode 100644 Final2/java/broker/etc/passwdVhost create mode 100644 Final2/java/broker/etc/persistent_config.xml create mode 100644 Final2/java/broker/etc/qpid-server.conf create mode 100644 Final2/java/broker/etc/qpid-server.conf.jpp create mode 100644 Final2/java/broker/etc/qpid.passwd create mode 100644 Final2/java/broker/etc/transient_config.xml create mode 100644 Final2/java/broker/etc/virtualhosts.xml create mode 100644 Final2/java/broker/pom.xml create mode 100755 Final2/java/broker/python-test.xml create mode 100644 Final2/java/broker/src/main/grammar/SelectorParser.jj create mode 100644 Final2/java/broker/src/main/java/log4j.properties create mode 100644 Final2/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/Main.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java create mode 100644 Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java create mode 100644 Final2/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java create mode 100644 Final2/java/client-java14/README.txt create mode 100644 Final2/java/client-java14/etc/sasl.properties create mode 100644 Final2/java/client-java14/pom.xml create mode 100644 Final2/java/client-java14/src/main/assembly/client-java14-bin.xml create mode 100644 Final2/java/client-java14/src/main/assembly/jar-with-dependencies.xml create mode 100644 Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java create mode 100644 Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java create mode 100644 Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java create mode 100644 Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java create mode 100644 Final2/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java create mode 100644 Final2/java/client/distribution/pom.xml create mode 100644 Final2/java/client/distribution/src/main/assembly/client-bin-tests.xml create mode 100644 Final2/java/client/distribution/src/main/assembly/client-bin.xml create mode 100644 Final2/java/client/distribution/src/main/assembly/client-java1.4-bin.xml create mode 100644 Final2/java/client/distribution/src/main/assembly/client-src.xml create mode 100644 Final2/java/client/example/bin/set_classpath.bat create mode 100755 Final2/java/client/example/bin/set_classpath.sh create mode 100644 Final2/java/client/example/pom.xml create mode 100644 Final2/java/client/example/source-jar.xml create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java create mode 100644 Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java create mode 100644 Final2/java/client/pom.xml create mode 100644 Final2/java/client/src/main/java/client.log4j create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQSession.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/Closeable.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/ChannelLimitReachedException.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/Connection.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/ConnectionListener.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/ConnectionURL.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/Message.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/MessageConsumer.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/MessageProducer.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/Session.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverMethod.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverSingleServer.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jndi/Example.properties create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jndi/NameParserImpl.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java create mode 100644 Final2/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/codec/Client.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/codec/Server.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/Connector.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargePublisher.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/topic/Config.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java create mode 100644 Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java create mode 100644 Final2/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/testutil/Config.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java create mode 100644 Final2/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java create mode 100644 Final2/java/client/test/bin/IBM-JNDI-Setup.bat create mode 100755 Final2/java/client/test/bin/IBM-JNDI-Setup.sh create mode 100644 Final2/java/client/test/bin/IBM-Publisher.bat create mode 100755 Final2/java/client/test/bin/IBM-Publisher.sh create mode 100644 Final2/java/client/test/bin/IBM-PutGet.bat create mode 100755 Final2/java/client/test/bin/IBM-PutGet.sh create mode 100644 Final2/java/client/test/bin/IBM-README.txt create mode 100644 Final2/java/client/test/bin/IBM-Receiver.bat create mode 100755 Final2/java/client/test/bin/IBM-Receiver.sh create mode 100644 Final2/java/client/test/bin/IBM-Sender.bat create mode 100755 Final2/java/client/test/bin/IBM-Sender.sh create mode 100644 Final2/java/client/test/bin/IBM-Subscriber.bat create mode 100755 Final2/java/client/test/bin/IBM-Subscriber.sh create mode 100755 Final2/java/client/test/bin/headersListener.sh create mode 100755 Final2/java/client/test/bin/headersListenerGroup.sh create mode 100755 Final2/java/client/test/bin/headersPublisher.sh create mode 100755 Final2/java/client/test/bin/run_many.sh create mode 100755 Final2/java/client/test/bin/serviceProvidingClient.sh create mode 100755 Final2/java/client/test/bin/serviceRequestingClient.sh create mode 100755 Final2/java/client/test/bin/testService.sh create mode 100755 Final2/java/client/test/bin/topicListener.sh create mode 100755 Final2/java/client/test/bin/topicPublisher.sh create mode 100644 Final2/java/client/test/etc/ApacheDS.properties create mode 100644 Final2/java/client/test/example_build.xml create mode 100644 Final2/java/cluster/doc/design.doc create mode 100644 Final2/java/cluster/pom.xml create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java create mode 100644 Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java create mode 100644 Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java create mode 100644 Final2/java/common/bin/qpid-run create mode 100644 Final2/java/common/etc/qpid-run.conf create mode 100644 Final2/java/common/etc/qpid-run.conf.dev create mode 100644 Final2/java/common/pom.xml create mode 100644 Final2/java/common/protocol-version.xml create mode 100644 Final2/java/common/readme.txt create mode 100644 Final2/java/common/src/main/java/log4j.properties create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQChannelException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/configuration/Configured.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQType.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/Content.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/MethodConverter_8_0.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/pool/Event.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/pool/Job.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/url/BindingURL.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/url/URLHelper.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/FileUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/ReflectionUtils.java create mode 100644 Final2/java/common/src/main/java/org/apache/qpid/util/ReflectionUtilsException.java create mode 100644 Final2/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert create mode 100644 Final2/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java create mode 100644 Final2/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java create mode 100644 Final2/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java create mode 100644 Final2/java/common/src/test/java/org/apache/qpid/session/TestSession.java create mode 100644 Final2/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java create mode 100644 Final2/java/distribution/pom.xml create mode 100644 Final2/java/distribution/src/main/assembly/bin-test.xml create mode 100644 Final2/java/distribution/src/main/assembly/bin.xml create mode 100644 Final2/java/distribution/src/main/assembly/management-eclipse-plugin-unix.xml create mode 100644 Final2/java/distribution/src/main/assembly/management-eclipse-plugin.xml create mode 100644 Final2/java/distribution/src/main/assembly/src.xml create mode 100644 Final2/java/doc/AMQBlazeDetailedDesign.vsd create mode 100644 Final2/java/doc/FramingClassDiagram.vsd create mode 100644 Final2/java/etc/coding_standards.xml create mode 100644 Final2/java/etc/license_header.txt create mode 100644 Final2/java/etc/log4j.xml create mode 100644 Final2/java/integrationtests/README.txt create mode 100644 Final2/java/integrationtests/docs/RunningSustainedTests.txt create mode 100644 Final2/java/integrationtests/jar-with-dependencies.xml create mode 100644 Final2/java/integrationtests/pom.xml create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase1DummyRun.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase2BasicP2P.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase3BasicPubSub.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase4P2PMessageSize.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/clienttestcases/TestCase5PubSubMessageSize.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase1DummyRun.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase2BasicP2P.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase3BasicPubSub.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase4P2PMessageSize.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/interop/testcases/InteropTestCase5PubSubMessageSize.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedClientTestCase.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/sustained/SustainedTestCase.java create mode 100644 Final2/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java create mode 100644 Final2/java/integrationtests/src/resources/sustained-log4j.xml create mode 100644 Final2/java/management/eclipse-plugin/META-INF/MANIFEST.MF create mode 100644 Final2/java/management/eclipse-plugin/README.txt create mode 100644 Final2/java/management/eclipse-plugin/bin/qpidmc.bat create mode 100755 Final2/java/management/eclipse-plugin/bin/qpidmc.sh create mode 100755 Final2/java/management/eclipse-plugin/bin/qpidmc_gtk.sh create mode 100755 Final2/java/management/eclipse-plugin/bin/qpidmc_motif.sh create mode 100644 Final2/java/management/eclipse-plugin/icons/Thumbs.db create mode 100644 Final2/java/management/eclipse-plugin/icons/add.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/delete.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/icon_ClosedFolder.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/icon_OpenFolder.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/mbean_view.png create mode 100644 Final2/java/management/eclipse-plugin/icons/notifications.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/qpidConnections.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/qpidmc.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/qpidmc16.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/qpidmc32.bmp create mode 100644 Final2/java/management/eclipse-plugin/icons/qpidmc32.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/reconnect.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/refresh.gif create mode 100644 Final2/java/management/eclipse-plugin/icons/splash.bmp create mode 100644 Final2/java/management/eclipse-plugin/icons/stop.gif create mode 100644 Final2/java/management/eclipse-plugin/plugin.properties create mode 100644 Final2/java/management/eclipse-plugin/plugin.xml create mode 100644 Final2/java/management/eclipse-plugin/pom.xml create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Activator.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Application.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationActionBarAdvisor.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchAdvisor.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationWorkbenchWindowAdvisor.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Constants.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedBean.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedObject.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ManagedServer.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/Perspective.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ServerRegistry.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AbstractAction.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/AddServer.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/CloseConnection.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/EditAttribute.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/ReconnectServer.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/Refresh.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/RemoveServer.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/actions/VersionAction.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/InfoRequiredException.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/exceptions/ManagementConsoleException.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientListener.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/ClientNotificationListener.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXServerRegistry.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/AttributeData.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ManagedAttributeModel.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationInfoModel.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationData.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/OperationDataModel.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/ParameterData.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/CRAMMD5HashedSaslClientFactory.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/ClientSaslFactory.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/JCAProvider.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/PlainSaslClient.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/SaslProvider.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UserPasswordCallbackHandler.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/sasl/UsernameHashedPasswordCallbackHandler.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/AttributesTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ConnectionTypeTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ExchangeTypeTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/INotificationViewer.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTypeTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanView.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NotificationsTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NumberVerifyListener.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/OperationTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/QueueTypeTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/TreeObject.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/VHNotificationsTabControl.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/ViewUtility.java create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/.eclipseproduct create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/eclipse.exe create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/eclipse.ini create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/license.eclipse.txt create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/sasl/MANIFEST.MF create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/startup.jar create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/unix/configuration/config.ini create mode 100644 Final2/java/management/eclipse-plugin/src/main/resources/win32/configuration/config.ini create mode 100644 Final2/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java create mode 100644 Final2/java/perftests/RunningPerformanceTests.txt create mode 100755 Final2/java/perftests/bin/run_many.sh create mode 100755 Final2/java/perftests/bin/topicListener.sh create mode 100755 Final2/java/perftests/bin/topicPublisher.sh create mode 100644 Final2/java/perftests/dist-zip.xml create mode 100644 Final2/java/perftests/distribution/pom.xml create mode 100644 Final2/java/perftests/distribution/src/main/assembly/performance.xml create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-1.sh create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-2.sh create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-3.sh create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-4.sh create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-5.sh create mode 100755 Final2/java/perftests/etc/scripts/CTQ-Qpid-6.sh create mode 100755 Final2/java/perftests/etc/scripts/PT-Qpid-13.sh create mode 100755 Final2/java/perftests/etc/scripts/PT-Qpid-14.sh create mode 100755 Final2/java/perftests/etc/scripts/sendAndWaitClient.sh create mode 100644 Final2/java/perftests/jar-with-dependencies.xml create mode 100644 Final2/java/perftests/pom.xml create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/client/message/TestMessageFactory.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/AbstractConfig.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/Connector.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/ConnectorConfig.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/oldtopic/Config.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/oldtopic/Listener.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/oldtopic/MessageFactory.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/oldtopic/Publisher.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingAsyncTestPerf.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingClient.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingDurableClient.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingLatencyTestPerf.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingSendOnlyClient.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/ping/PingTestPerf.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongBouncer.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongProducer.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/requestreply/PingPongTestPerf.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/topic/Config.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/topic/Listener.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/topic/MessageFactory.java create mode 100644 Final2/java/perftests/src/main/java/org/apache/qpid/topic/Publisher.java create mode 100644 Final2/java/perftests/src/main/java/perftests.log4j create mode 100644 Final2/java/pom.xml create mode 100644 Final2/java/release-docs/RELEASE_NOTES.txt create mode 100644 Final2/java/resources/DISCLAIMER create mode 100644 Final2/java/resources/LICENSE create mode 100644 Final2/java/resources/META-INF/DISCLAIMER create mode 100644 Final2/java/resources/META-INF/LICENSE create mode 100644 Final2/java/resources/META-INF/NOTICE create mode 100644 Final2/java/resources/NOTICE create mode 100644 Final2/java/resources/README create mode 100644 Final2/java/systests/distribution/pom.xml create mode 100644 Final2/java/systests/distribution/src/main/assembly/systests.xml create mode 100644 Final2/java/systests/etc/bin/testclients.sh create mode 100644 Final2/java/systests/pom.xml create mode 100644 Final2/java/systests/src/main/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/exchange/HeadersExchangeTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/exchange/ReturnUnroutableMandatoryMessageTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/failure/DeadlockTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/failure/HeapExhaustion.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/protocol/MaxChannelsTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/protocol/MockIoSession.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/MessageReturnTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/MockProtocolSession.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/PersistentTestManual.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/QueueDepthWithSelectorTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/queue/TimeToLiveTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/txn/TxnBufferTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/txn/TxnTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/util/AveragedRun.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/util/RunStats.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/util/TestApplicationRegistry.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/server/util/TimedRun.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/VMBrokerSetup.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/VMTestCase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/client/QueueBrowserTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/Assertion.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/AssertionBase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/Circuit.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEnd.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/DropInTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkClientBaseCase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/MessageMonitor.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/TestClientDetails.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchThread.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/LocalClockSynchronizer.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockReference.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/UDPClockSynchronizer.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedCircuitImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/DistributedTestDecorator.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/FanOutTestDecorator.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/OptOutTestCase.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClientControlledTest.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/listeners/XMLTestListener.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/package.html create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/testutil/QpidClientConnectionHelper.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/util/ClasspathScanner.java create mode 100644 Final2/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java create mode 100644 Final2/java/systests/src/main/java/systests.log4j create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/protocol/TestProtocolInitiation.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/queue/QueuePerfTest.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/queue/SendPerfTest.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/server/util/ConcurrentTest.java create mode 100644 Final2/java/systests/src/old_test/java/org/apache/qpid/test/unit/ack/DisconnectAndRedeliverTest.java (limited to 'Final2/java') diff --git a/Final2/java/broker/bin/msTool.sh b/Final2/java/broker/bin/msTool.sh new file mode 100755 index 0000000000..73b1eb2ec7 --- /dev/null +++ b/Final2/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-incubating.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/Final2/java/broker/bin/qpid-passwd b/Final2/java/broker/bin/qpid-passwd new file mode 100644 index 0000000000..f046252522 --- /dev/null +++ b/Final2/java/broker/bin/qpid-passwd @@ -0,0 +1,30 @@ +#!/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. +# + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-incubating.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/Final2/java/broker/bin/qpid-server b/Final2/java/broker/bin/qpid-server new file mode 100644 index 0000000000..f1f4d72e64 --- /dev/null +++ b/Final2/java/broker/bin/qpid-server @@ -0,0 +1,31 @@ +#!/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. +# + +# Set classpath to include Qpid jar with all required jars in manifest +QPID_LIBS=$QPID_HOME/lib/qpid-incubating.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 org.apache.qpid.server.Main "$@" diff --git a/Final2/java/broker/bin/qpid-server-bdb.bat b/Final2/java/broker/bin/qpid-server-bdb.bat new file mode 100644 index 0000000000..8964e577df --- /dev/null +++ b/Final2/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/Final2/java/broker/bin/qpid-server.bat b/Final2/java/broker/bin/qpid-server.bat new file mode 100644 index 0000000000..a99022cd2d --- /dev/null +++ b/Final2/java/broker/bin/qpid-server.bat @@ -0,0 +1,70 @@ +@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 + +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 + +rem Slurp the command line arguments. This loop allows for an unlimited number +rem of agruments (up to the command line limit, anyway). +set QPID_ARGS=%1 +if ""%1""=="""" goto runCommand +shift +:loop +if ""%1""=="""" goto runCommand +set QPID_ARGS=%QPID_ARGS% %1 +shift +goto loop + +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 LAUNCH_JAR=%QPID_HOME%\lib\qpid-incubating.jar +set MODULE_JARS=%QPID_MODULE_JARS% +"%JAVA_HOME%\bin\java" -server -Xmx1024m %QPID_OPTS% -DQPID_HOME="%QPID_HOME%" -cp "%LAUNCH_JAR%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS% + +:end diff --git a/Final2/java/broker/bin/qpid.start b/Final2/java/broker/bin/qpid.start new file mode 100644 index 0000000000..99f2d89f64 --- /dev/null +++ b/Final2/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. +# + +qpid-server -run:debug "$@" \ No newline at end of file diff --git a/Final2/java/broker/bin/qpid.stop b/Final2/java/broker/bin/qpid.stop new file mode 100644 index 0000000000..6482fc3293 --- /dev/null +++ b/Final2/java/broker/bin/qpid.stop @@ -0,0 +1,137 @@ +#!/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 PROGRAM and attempts to quit it +# + +MAX_ATTEMPTS=1 +SLEEP_DELAY=1 +PROGRAM="DQPID" + + +# +# Print what is going to be done +# +printActions() +{ +#ps=`ps o command p $1|grep $PROGRAM` +ps=`ps -o args -p $1|grep $PROGRAM` +echo "Attempting to kill: $ps" +} + +# +# Forcably Quit the specified PID($1) +# +forceQuit() +{ +kill -9 $1 +} + + +# +# Gracefully ask the PID($1) to quit +# +quit() +{ +kill $1 +} + +# +# Grep the ps log for the PID ($1) to ensure that it has quit +# +lookup() +{ +result=`ps -o args -p $1 |grep -v grep |grep $PROGRAM |wc -l` +} + +# +# 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 $1 +} + + + +# +# Verify the PID($1) is available +# +verifyPid() +{ +lookup $1 +if [[ $[$result] == 1 ]] ; then + brokerspid=$1 +else + echo "Unable to locate Qpid Process with PID $1" + exit -1 +fi +} + +# +# Main Run +# + +# Check if we are killing all qpid pids or just one. +if [[ $# == 0 ]] ; then + echo "Killing All Qpid Brokers for user: '$USER'" + qpid.stopall + exit $? +else + verifyPid $1 +fi + +printActions $brokerspid + +# 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" + exit 0 +else + + # Now attempt to force quit the process + attempt=0 + 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 diff --git a/Final2/java/broker/bin/qpid.stopall b/Final2/java/broker/bin/qpid.stopall new file mode 100644 index 0000000000..d71f591de8 --- /dev/null +++ b/Final2/java/broker/bin/qpid.stopall @@ -0,0 +1,74 @@ +#!/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 +# + +PROGRAM="DQPID" + +# +# grep ps for instances of $PROGRAM and collect PIDs +# +lookup() +{ +#pids=`ps o pid,command | grep $PROGRAM | grep -v grep | cut -d ' ' -f 1` +pids=`ps -ef |grep $USER | grep $PROGRAM | grep -v grep | awk '{print $2}'` +result=`echo -n $pids | wc -w` +} + + +# +# Show the PS output for given set of pids +# +showPids() +{ +ps -o user,pid,args -p $pids +} + + +# +# Main Run +# + +lookup + +if [[ $[$result] == 0 ]] ; then + echo "No Qpid Brokers found running under user '$USER'" + exit 0 +fi + +for pid in $pids ; do + +qpid.stop $pid + +done + +# Check we have quit all +lookup + +if [[ $[$result] == 0 ]] ; then + echo "All Qpid brokers successfully quit" +else + echo "Some brokers were not quit" + showPids $pids +fi diff --git a/Final2/java/broker/bin/run.bat b/Final2/java/broker/bin/run.bat new file mode 100755 index 0000000000..5b0aa0f23b --- /dev/null +++ b/Final2/java/broker/bin/run.bat @@ -0,0 +1,31 @@ +@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 +set CORE_CLASSES=..\classes;..\testclasses + +set COMMONDIR=..\..\common + +set COMMONLIB=%COMMONDIR%\lib\slf4j\slf4j-simple.jar;%COMMONDIR%\lib\logging-log4j\log4j-1.2.9.jar;%COMMONDIR%\lib\mina\mina-core-0.9.2.jar + +set DIST=..\lib\bdb\je-3.0.12.jar;..\dist\amqpd.jar;..\..\client\dist\amqp-common.jar + +set CP=%CORE_CLASSES%;%DIST%;%COMMONLIB% + +"%JAVA_HOME%\bin\java" -Xmx512m -Damqj.logging.level=INFO -cp %CP% %* diff --git a/Final2/java/broker/bin/run.sh b/Final2/java/broker/bin/run.sh new file mode 100755 index 0000000000..6b62049e94 --- /dev/null +++ b/Final2/java/broker/bin/run.sh @@ -0,0 +1,44 @@ +#!/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. +# + + +COMMONDIR=../../common + +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/slf4j/slf4j-simple.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/logging-log4j/log4j-1.2.13.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-core-0.9.5-SNAPSHOT.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-filter-ssl-0.9.5-SNAPSHOT.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-collections/commons-collections-3.1.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-cli/commons-cli-1.0.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-configuration/commons-configuration-1.2.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging-api.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-lang/commons-lang-2.1.jar +COMMONLIB=$COMMONLIB:$COMMONDIR/lib/junit/junit-4.0.jar + +DIST=../lib/bdb/je-3.0.12.jar:../dist/amqpd-tests.jar:../dist/amqpd.jar:../../client/dist/amqp-common.jar + +CP=../intellijclasses:$DIST:$COMMONLIB + +if [ "$(uname -a | fgrep Cygwin)" != "" ]; then + CP=$(cygpath --mixed --path $CP) +fi + +"$JAVA_HOME/bin/java" -Xmx512m -Dprepopulate=10 -Damqj.logging.level=INFO -cp $CP $* diff --git a/Final2/java/broker/bin/runAll b/Final2/java/broker/bin/runAll new file mode 100644 index 0000000000..4ced1d263b --- /dev/null +++ b/Final2/java/broker/bin/runAll @@ -0,0 +1,37 @@ +#!/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. +# + +doRun() +{ + class=$1 + shift + echo + echo ================================================================================ + echo Running $class + ./run.sh $class "$@" +} + +# parameters are: clients messages iterations +doRun org.apache.qpid.server.queue.SendPerfTest 10 1000 100 + +doRun org.apache.qpid.server.queue.QueuePerfTest + +doRun org.apache.qpid.server.queue.QueueConcurrentPerfTest diff --git a/Final2/java/broker/distribution/pom.xml b/Final2/java/broker/distribution/pom.xml new file mode 100644 index 0000000000..8737d943eb --- /dev/null +++ b/Final2/java/broker/distribution/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + org.apache.qpid + qpid-broker-distribution + jar + 1.0-incubating-M2 + Qpid Broker Distributions + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + + + + .. + 1.5 + ${pom.version} + ${project.build.directory} + + + + + org.apache.qpid + qpid-broker + jar + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.source.version} + ${java.source.version} + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${assembly.version} + + + src/main/assembly/broker-bin.xml + + qpid-${pom.version} + ${qpid.targetDir} + gnu + + + + + org.apache.maven.plugins + maven-jar-plugin + + qpid-incubating + + + true + + + + + + + + + + + maven-assembly-plugin + + + distribution-package + package + + single + + + + src/main/assembly/broker-bin.xml + src/main/assembly/broker-src.xml + + + + + + + + + + + + tests + + + + org.apache.qpid + qpid-broker + test-jar + ${project.version} + + + + + + + maven-assembly-plugin + + + distribution-package + package + + single + + + + src/main/assembly/broker-bin-tests.xml + + + + + + + + + + + diff --git a/Final2/java/broker/distribution/src/main/assembly/broker-bin-tests.xml b/Final2/java/broker/distribution/src/main/assembly/broker-bin-tests.xml new file mode 100644 index 0000000000..fa017d6232 --- /dev/null +++ b/Final2/java/broker/distribution/src/main/assembly/broker-bin-tests.xml @@ -0,0 +1,116 @@ + + + + java-broker-bin-with-tests + false + + tar.gz + zip + + + + + + ../../resources + qpid-${qpid.version} + + DISCLAIMER + LICENSE.txt + NOTICE.txt + README.txt + + + + + ../../release-docs + qpid-${qpid.version}/docs + + RELEASE_NOTES.txt + + + + + + ../src/test + qpid-${qpid.version}/src + + **/*.java + + + + + + ../bin/ + qpid-${qpid.version}/bin + + **/* + + 777 + + + + + ../etc/ + qpid-${qpid.version}/etc + + **/* + + 420 + + + + + target + qpid-${qpid.version}/lib + + qpid-incubating.jar + + + + + + + + + ../../common/bin/qpid-run + qpid-${qpid.version}/bin + qpid-run + 493 + + + + + ../../common/etc/qpid-run.conf + qpid-${qpid.version}/etc + qpid-run.conf + 420 + + + + + + qpid-${qpid.version}/lib + false + + org.apache.qpid:qpid-broker-distribution + + + + diff --git a/Final2/java/broker/distribution/src/main/assembly/broker-bin.xml b/Final2/java/broker/distribution/src/main/assembly/broker-bin.xml new file mode 100644 index 0000000000..e66190a3f4 --- /dev/null +++ b/Final2/java/broker/distribution/src/main/assembly/broker-bin.xml @@ -0,0 +1,183 @@ + + + + + java-broker-bin + false + + tar.gz + zip + + + + + + ../../resources + qpid-${qpid.version} + + DISCLAIMER + LICENSE.txt + NOTICE.txt + README.txt + + + + ../.. + qpid-${qpid.version} + + *.txt + + + + ../../src/main/release-docs + qpid-${qpid.version}/docs + + RELEASE_NOTES.txt + + + + + + target + qpid-${qpid.version}/lib + + qpid-incubating.jar + + + + + + + ../../common/etc/qpid-run.conf + qpid-${qpid.version}/etc + qpid-run.conf + 420 + + + ../etc/config.xml + qpid-${qpid.version}/etc + config.xml + 420 + + + ../etc/jmxremote.access + qpid-${qpid.version}/etc + jmxremote.access + 420 + + + ../etc/log4j.xml + qpid-${qpid.version}/etc + log4j.xml + 420 + + + ../etc/passwd + qpid-${qpid.version}/etc + passwd + 420 + + + ../etc/qpid-server.conf + qpid-${qpid.version}/etc + qpid-server.conf + 420 + + + ../etc/virtualhosts.xml + qpid-${qpid.version}/etc + virtualhosts.xml + 420 + + + ../../common/bin/qpid-run + qpid-${qpid.version}/bin + qpid-run + 473 + + + ../bin/qpid-passwd + qpid-${qpid.version}/bin + qpid-passwd + 473 + + + ../bin/qpid-server + qpid-${qpid.version}/bin + qpid-server + 473 + + + ../bin/qpid-server.bat + qpid-${qpid.version}/bin + qpid-server.bat + 473 + + + ../bin/run.bat + qpid-${qpid.version}/bin + run.bat + 473 + + + ../bin/run.sh + qpid-${qpid.version}/bin + run.sh + 473 + + + ../bin/runAll + qpid-${qpid.version}/bin + runAll + 473 + + + + + + qpid-${qpid.version}/lib + false + + + org.apache.qpid:qpid-broker-distribution + org.apache.qpid.management:org.apache.qpid.management.ui + org.eclipse.core:org.eclipse.core.commands + org.eclipse.core:org.eclipse.core.contenttype + org.eclipse.core:org.eclipse.core.expressions + org.eclipse.core:org.eclipse.core.jobs + org.eclipse.core:org.eclipse.core.runtime + org.eclipse.core:org.eclipse.core.runtime.compatibility.auth + org.eclipse.core:org.eclipse.core.runtime.compatibility.registry + org.eclipse.equinox:org.eclipse.equinox.common + org.eclipse.equinox:org.eclipse.equinox.preferences + org.eclipse.equinox:org.eclipse.equinox.registry + org.eclipse.help:org.eclipse.help + org.eclipse.jface:org.eclipse.jface + org.eclipse.osgi:org.eclipse.osgi + org.eclipse.swt:org.eclipse.swt + org.eclipse.swt:org.eclipse.swt.win32.win32.x86 + org.eclipse.ui:org.eclipse.ui + org.eclipse.ui:org.eclipse.ui.forms + org.eclipse.ui:org.eclipse.ui.workbench + + + + diff --git a/Final2/java/broker/distribution/src/main/assembly/broker-src.xml b/Final2/java/broker/distribution/src/main/assembly/broker-src.xml new file mode 100644 index 0000000000..28a22c3851 --- /dev/null +++ b/Final2/java/broker/distribution/src/main/assembly/broker-src.xml @@ -0,0 +1,78 @@ + + + + + java-broker-src + false + + tar.gz + zip + + + + + + ../../resources + qpid-${qpid.version}-src + + DISCLAIMER + LICENSE.txt + licenses/*.* + NOTICE.txt + README.txt + BUILDING.txt + + + + + .. + qpid-${qpid.version}-src + + **/* + + + + build.xml + distribution/build.xml + benchmark + benchmark/**/* + **/target + **/target/**/* + **/build + **/build/**/* + **/.settings + **/.classpath + **/.project + **/.wtpmodules + **/surefire* + **/cobertura.ser + bin + bin/* + lib + lib/**/* + **/var/journal + **/build.out* + **/eclipse-plugin/bin/** + **/eclipse-plugin/plugins/** + **/eclipse-plugin/src/main/resources/** + + + + diff --git a/Final2/java/broker/etc/access b/Final2/java/broker/etc/access new file mode 100644 index 0000000000..58b7443fa9 --- /dev/null +++ b/Final2/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/Final2/java/broker/etc/config.xml b/Final2/java/broker/etc/config.xml new file mode 100644 index 0000000000..b5b81bbeb0 --- /dev/null +++ b/Final2/java/broker/etc/config.xml @@ -0,0 +1,175 @@ + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + + true + nio + 5672 + 8672 + 32768 + 32768 + + + true + 8999 + false + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase + + + passwordFile + ${conf}/passwdVhost + + + + + + + + + org.apache.qpid.server.security.access.AllowAll + + + ${conf}/jmxremote.access + passwordfile + + + + + + localhost + + + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + passwordfile + + PLAIN + + + org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager + + + principalDatabase + passwordfile + + + defaultAccessManager + DenyAll + + + + + + + + + development + + + org.apache.qpid.server.store.MemoryMessageStore + + + passwordfile-notusedyet + PLAIN + CRAM-MD5 + + + + + + test + + + org.apache.qpid.server.store.MemoryMessageStore + + + passwordfile-notusedyet + PLAIN + CRAM-MD5 + + + org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager + + + principalDatabase + rubbish-to-cause-default + + + + + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts.xml + + + diff --git a/Final2/java/broker/etc/debug.log4j.xml b/Final2/java/broker/etc/debug.log4j.xml new file mode 100644 index 0000000000..e8fd7e119d --- /dev/null +++ b/Final2/java/broker/etc/debug.log4j.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Final2/java/broker/etc/jmxremote.access b/Final2/java/broker/etc/jmxremote.access new file mode 100644 index 0000000000..1a51a6991b --- /dev/null +++ b/Final2/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/Final2/java/broker/etc/log4j.xml b/Final2/java/broker/etc/log4j.xml new file mode 100644 index 0000000000..af8e7a8293 --- /dev/null +++ b/Final2/java/broker/etc/log4j.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Final2/java/broker/etc/md5passwd b/Final2/java/broker/etc/md5passwd new file mode 100644 index 0000000000..6a149919de --- /dev/null +++ b/Final2/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/Final2/java/broker/etc/mstool-log4j.xml b/Final2/java/broker/etc/mstool-log4j.xml new file mode 100644 index 0000000000..8c46010e2d --- /dev/null +++ b/Final2/java/broker/etc/mstool-log4j.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Final2/java/broker/etc/passwd b/Final2/java/broker/etc/passwd new file mode 100644 index 0000000000..966a16153d --- /dev/null +++ b/Final2/java/broker/etc/passwd @@ -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 diff --git a/Final2/java/broker/etc/passwdVhost b/Final2/java/broker/etc/passwdVhost new file mode 100644 index 0000000000..48ce8299b6 --- /dev/null +++ b/Final2/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/Final2/java/broker/etc/persistent_config.xml b/Final2/java/broker/etc/persistent_config.xml new file mode 100644 index 0000000000..178a73515c --- /dev/null +++ b/Final2/java/broker/etc/persistent_config.xml @@ -0,0 +1,132 @@ + + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + true + nio + 5672 + 8672 + 32768 + 32768 + + + true + 8999 + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase + + + passwordFile + ${conf}/passwdVhost + + + + + + + org.apache.qpid.server.security.access.AllowAll + + + ${conf}/jmxremote.access + passwordfile + + + + + + localhost + + + org.apache.qpid.server.store.berkeleydb.BDBMessageStore + ${work}/bdbstore/localhost-store + + + + + org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager + + + principalDatabase + passwordfile + + + defaultAccessManager + DenyAll + + + + + + + + + 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/Final2/java/broker/etc/qpid-server.conf b/Final2/java/broker/etc/qpid-server.conf new file mode 100644 index 0000000000..c310094817 --- /dev/null +++ b/Final2/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-incubating.jar:$QPID_HOME/lib/bdbstore-launch.jar + +export JAVA=java \ + JAVA_VM=-server \ + JAVA_MEM=-Xmx1024m \ + CLASSPATH=$QPID_LIBS diff --git a/Final2/java/broker/etc/qpid-server.conf.jpp b/Final2/java/broker/etc/qpid-server.conf.jpp new file mode 100644 index 0000000000..3ed2431ef3 --- /dev/null +++ b/Final2/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/Final2/java/broker/etc/qpid.passwd b/Final2/java/broker/etc/qpid.passwd new file mode 100644 index 0000000000..79b5e11777 --- /dev/null +++ b/Final2/java/broker/etc/qpid.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:CE4DQ6BIb/BVMN9scFyLtA== +admin:ISMvKXpXpadDiUoOSoAfww== +user:CE4DQ6BIb/BVMN9scFyLtA== + diff --git a/Final2/java/broker/etc/transient_config.xml b/Final2/java/broker/etc/transient_config.xml new file mode 100644 index 0000000000..164d66cd1b --- /dev/null +++ b/Final2/java/broker/etc/transient_config.xml @@ -0,0 +1,128 @@ + + + + ${QPID_HOME} + ${QPID_WORK} + ${prefix}/etc + + true + nio + 5672 + 8672 + 32768 + 32768 + + + true + 8999 + + + + false + false + 65535 + false + + + + + + passwordfile + org.apache.qpid.server.security.auth.database.PlainPasswordVhostFilePrincipalDatabase + + + passwordFile + ${conf}/passwdVhost + + + + + + org.apache.qpid.server.security.access.AllowAll + + + ${conf}/jmxremote.access + passwordfile + + + + + + localhost + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + org.apache.qpid.server.security.access.PrincipalDatabaseAccessManager + + + principalDatabase + passwordfile + + + defaultAccessManager + DenyAll + + + + + + + + + development + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + test + + + org.apache.qpid.server.store.MemoryMessageStore + + + + + + + 0 + 2.0 + + + true + + + ${conf}/virtualhosts.xml + + + diff --git a/Final2/java/broker/etc/virtualhosts.xml b/Final2/java/broker/etc/virtualhosts.xml new file mode 100644 index 0000000000..f62ec3f5d7 --- /dev/null +++ b/Final2/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/Final2/java/broker/pom.xml b/Final2/java/broker/pom.xml new file mode 100644 index 0000000000..914a272571 --- /dev/null +++ b/Final2/java/broker/pom.xml @@ -0,0 +1,256 @@ + + + 4.0.0 + org.apache.qpid + qpid-broker + jar + 1.0-incubating-M2 + Qpid Broker + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + ../pom.xml + + + + .. + + + + + + org.apache.qpid + qpid-common + + + + log4j + log4j + + + + org.slf4j + slf4j-log4j12 + 1.4.0 + + + + commons-cli + commons-cli + + + commons-logging + commons-logging + + + + + + commons-configuration + commons-configuration + + + javax.servlet + servlet-api + + + dom4j + dom4j + + + commons-beanutils + commons-beanutils + + + commons-beanutils + commons-beanutils-core + + + commons-digester + commons-digester + + + xerces + xercesImpl + + + xml-apis + xml-apis + + + + + + commons-lang + commons-lang + + + + + junit + junit + test + + + + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + org.codehaus.mojo + javacc-maven-plugin + 2.0 + + + generate-sources + + ${basedir}/src/main/grammar + ${basedir}/target/generated-sources + org.apache.qpid.server.filter.jms.selector + + + javacc + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + amqj.noAutoCreateVMBroker + true + + + amqj.logging.level + ${amqj.logging.level} + + + log4j.configuration + file:///${basedir}/src/main/java/log4j.properties + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + test-jar + + + + + + + + + src/ + false + src/test/java + + **/*.java + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + ${antrun.version} + + + ant + ant-nodeps + 1.6.5 + + + + + + python_test + test + + + + + + + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/Final2/java/broker/python-test.xml b/Final2/java/broker/python-test.xml new file mode 100755 index 0000000000..e044207948 --- /dev/null +++ b/Final2/java/broker/python-test.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Final2/java/broker/src/main/grammar/SelectorParser.jj b/Final2/java/broker/src/main/grammar/SelectorParser.jj new file mode 100644 index 0000000000..adec1b348d --- /dev/null +++ b/Final2/java/broker/src/main/grammar/SelectorParser.jj @@ -0,0 +1,604 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + // + // 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).initCause(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","_", "$"])* > +} + +// ---------------------------------------------------------------------------- +// 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; + PropertyExpression left=null; +} +{ + ( + t = + { + left = new PropertyExpression(t.image); + } + ) + { + return left; + } +} diff --git a/Final2/java/broker/src/main/java/log4j.properties b/Final2/java/broker/src/main/java/log4j.properties new file mode 100644 index 0000000000..87f04f4991 --- /dev/null +++ b/Final2/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=info +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n diff --git a/Final2/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java b/Final2/java/broker/src/main/java/org/apache/log4j/QpidCompositeRollingAppender.java new file mode 100644 index 0000000000..7e0c4defe1 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java b/Final2/java/broker/src/main/java/org/apache/qpid/configuration/Configuration.java new file mode 100644 index 0000000000..40ff590a0a --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java new file mode 100644 index 0000000000..9335723bc5 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -0,0 +1,244 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +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 = new AMQQueue(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost()); + if (queue.isDurable() && !queue.isAutoDelete()) + { + _messageStore.createQueue(queue); + } + + Configuration virtualHostDefaultQueueConfiguration = + VirtualHostConfiguration.getDefaultQueueConfiguration(queue); + if (virtualHostDefaultQueueConfiguration != null) + { + Configurator.configure(queue, virtualHostDefaultQueueConfiguration); + } + + _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(new AMQShortString(queueName)); + + } + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java new file mode 100644 index 0000000000..d3b459c48a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -0,0 +1,989 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.UnacknowledgedMessage; +import org.apache.qpid.server.ack.UnacknowledgedMessageMap; +import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl; +import org.apache.qpid.server.exchange.MessageRouter; +import org.apache.qpid.server.exchange.NoRouteException; +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.MessageHandleFactory; +import org.apache.qpid.server.queue.Subscription; +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.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +public class AMQChannel +{ + public static final int DEFAULT_PREFETCH = 5000; + + private static final Logger _log = Logger.getLogger(AMQChannel.class); + + private final int _channelId; + + // private boolean _transactional; + + private long _prefetch_HighWaterMark; + + private long _prefetch_LowWaterMark; + + private long _prefetchSize; + + /** + * 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 AtomicLong _deliveryTag = new AtomicLong(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 AMQMessage _currentMessage; + + /** Maps from consumer tag to queue instance. Allows us to unsubscribe from a queue. */ + private final Map _consumerTag2QueueMap = new HashMap(); + + private final MessageStore _messageStore; + + private UnacknowledgedMessageMap _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(DEFAULT_PREFETCH); + + private final AtomicBoolean _suspended = new AtomicBoolean(false); + + private final MessageRouter _exchanges; + + private TransactionalContext _txnContext, _nonTransactedContext; + + /** + * 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(); + + private Set _browsedAcks = new HashSet(); + + // Why do we need this reference ? - ritchiem + private final AMQProtocolSession _session; + private boolean _closing; + + public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore, MessageRouter exchanges) + throws AMQException + { + _session = session; + _channelId = channelId; + _storeContext = new StoreContext("Session: " + session.getClientIdentifier() + "; channel: " + channelId); + _prefetch_HighWaterMark = DEFAULT_PREFETCH; + _prefetch_LowWaterMark = _prefetch_HighWaterMark / 2; + _messageStore = messageStore; + _exchanges = exchanges; + // by default the session is non-transactional + _txnContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks); + } + + /** Sets this channel to be part of a local transaction */ + public void setLocalTransactional() + { + _txnContext = new LocalTransactionalContext(_messageStore, _storeContext, _returnMessages); + } + + 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 long getPrefetchCount() + { + return _prefetch_HighWaterMark; + } + + public void setPrefetchCount(long prefetchCount) + { + _prefetch_HighWaterMark = prefetchCount; + } + + public long getPrefetchSize() + { + return _prefetchSize; + } + + public void setPrefetchSize(long prefetchSize) + { + _prefetchSize = prefetchSize; + } + + public long getPrefetchLowMarkCount() + { + return _prefetch_LowWaterMark; + } + + public void setPrefetchLowMarkCount(long prefetchCount) + { + _prefetch_LowWaterMark = prefetchCount; + } + + public long getPrefetchHighMarkCount() + { + return _prefetch_HighWaterMark; + } + + public void setPrefetchHighMarkCount(long prefetchCount) + { + _prefetch_HighWaterMark = prefetchCount; + } + + public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher) throws AMQException + { + + _currentMessage = new AMQMessage(_messageStore.getNewMessageId(), info, _txnContext); + _currentMessage.setPublisher(publisher); + } + + public void publishContentHeader(ContentHeaderBody contentHeaderBody, AMQProtocolSession protocolSession) + throws AMQException + { + if (_currentMessage == null) + { + throw new AMQException("Received content header without previously receiving a BasicPublish frame"); + } + else + { + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "Content header received on channel " + _channelId); + } + + _currentMessage.setContentHeaderBody(contentHeaderBody); + _currentMessage.setExpiration(); + + routeCurrentMessage(); + _currentMessage.routingComplete(_messageStore, _storeContext, _messageHandleFactory); + + // check and deliver if header says body length is zero + if (contentHeaderBody.bodySize == 0) + { + _txnContext.messageProcessed(protocolSession); + _currentMessage = null; + } + } + } + + public void publishContentBody(ContentBody contentBody, AMQProtocolSession protocolSession) throws AMQException + { + if (_currentMessage == null) + { + throw new AMQException("Received content body without previously receiving a JmsPublishBody"); + } + + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "Content body received on channel " + _channelId); + } + + try + { + + // returns true iff the message was delivered (i.e. if all data was + // received + if (_currentMessage.addContentBodyFrame(_storeContext, + protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToContentChunk( + contentBody))) + { + // 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(protocolSession); + _currentMessage = null; + } + } + 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 + { + _exchanges.routeContent(_currentMessage); + } + catch (NoRouteException e) + { + _returnMessages.add(e); + } + } + + public long getNextDeliveryTag() + { + return _deliveryTag.incrementAndGet(); + } + + 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 session the protocol session of the subscriber + * @param noLocal Flag stopping own messages being receivied. + * @param exclusive Flag requesting exclusive access to the queue + * @param acks Are acks enabled for this subscriber + * @param filters Filters to apply to this subscriber + * + * @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, AMQProtocolSession session, boolean acks, + FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException, ConsumerTagNotUniqueException + { + if (tag == null) + { + tag = new AMQShortString("sgen_" + getNextConsumerTag()); + } + + if (_consumerTag2QueueMap.containsKey(tag)) + { + throw new ConsumerTagNotUniqueException(); + } + + queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive); + _consumerTag2QueueMap.put(tag, queue); + + return tag; + } + + public void unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException + { + if (_log.isDebugEnabled()) + { + _log.debug("Unacked Map Dump size:" + _unacknowledgedMessageMap.size()); + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + { + + public boolean callback(UnacknowledgedMessage message) throws AMQException + { + _log.debug(message); + + return true; + } + + public void visitComplete() + { } + }); + } + + AMQQueue q = _consumerTag2QueueMap.remove(consumerTag); + if (q != null) + { + q.unregisterProtocolSession(session, _channelId, consumerTag); + } + } + + /** + * Called from the protocol session to close this channel and clean up. T + * + * @param session The session to close + * + * @throws AMQException if there is an error during closure + */ + public void close(AMQProtocolSession session) throws AMQException + { + _txnContext.rollback(); + unsubscribeAllConsumers(session); + requeue(); + + setClosing(true); + } + + private void setClosing(boolean closing) + { + _closing = closing; + } + + private void unsubscribeAllConsumers(AMQProtocolSession session) throws AMQException + { + if (_log.isInfoEnabled()) + { + if (!_consumerTag2QueueMap.isEmpty()) + { + _log.info("Unsubscribing all consumers on channel " + toString()); + } + else + { + _log.info("No consumers to unsubscribe on channel " + toString()); + } + } + + for (Map.Entry me : _consumerTag2QueueMap.entrySet()) + { + if (_log.isInfoEnabled()) + { + _log.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString()); + } + + me.getValue().unregisterProtocolSession(session, _channelId, me.getKey()); + } + + _consumerTag2QueueMap.clear(); + } + + /** + * Add a message to the channel-based list of unacknowledged messages + * + * @param message the message that was delivered + * @param deliveryTag the delivery tag used when delivering the message (see protocol spec for description of the + * delivery tag) + * @param consumerTag The tag for the consumer that is to acknowledge this message. + * @param queue the queue from which the message was delivered + */ + public void addUnacknowledgedMessage(AMQMessage message, long deliveryTag, AMQShortString consumerTag, AMQQueue queue) + { + if (_log.isDebugEnabled()) + { + if (queue == null) + { + _log.debug("Adding unacked message with a null queue:" + message.debugIdentity()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + " Adding unacked message(" + message.toString() + " DT:" + deliveryTag + + ") with a queue(" + queue + ") for " + consumerTag); + } + } + } + + synchronized (_unacknowledgedMessageMap.getLock()) + { + _unacknowledgedMessageMap.add(deliveryTag, new UnacknowledgedMessage(queue, message, consumerTag, deliveryTag)); + checkSuspension(); + } + } + + 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)) + { + // if (_nonTransactedContext == null) + { + _nonTransactedContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks); + } + + deliveryContext = _nonTransactedContext; + } + else + { + deliveryContext = _txnContext; + } + } + + for (UnacknowledgedMessage unacked : messagesToBeDelivered) + { + if (unacked.queue != null) + { + // Ensure message is released for redelivery + unacked.message.release(unacked.queue); + + // Mark message redelivered + unacked.message.setRedelivered(true); + + // Deliver Message + deliveryContext.deliver(unacked.message, unacked.queue, false); + + // Should we allow access To the DM to directy deliver the message? + // As we don't need to check for Consumers or worry about incrementing the message count? + // unacked.queue.getDeliveryManager().deliver(_storeContext, unacked.queue.getName(), unacked.message, false); + } + } + + } + + /** + * Requeue a single message + * + * @param deliveryTag The message to requeue + * + * @throws AMQException If something goes wrong. + */ + public void requeue(long deliveryTag) throws AMQException + { + UnacknowledgedMessage unacked = _unacknowledgedMessageMap.remove(deliveryTag); + + if (unacked != null) + { + + // Ensure message is released for redelivery + if (unacked.queue != null) + { + unacked.message.release(unacked.queue); + } + + // Mark message redelivered + unacked.message.setRedelivered(true); + + // 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)) + { + // if (_nonTransactedContext == null) + { + _nonTransactedContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks); + } + + deliveryContext = _nonTransactedContext; + } + else + { + deliveryContext = _txnContext; + } + + if (unacked.queue != null) + { + // Redeliver the messages to the front of the queue + deliveryContext.deliver(unacked.message, unacked.queue, true); + // 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.message.debugIdentity() + + "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message."); + // _log.error("Requested requeue of message:" + deliveryTag + + // " but no queue defined using DeadLetter queue:" + getDeadLetterQueue()); + // + // deliveryContext.deliver(unacked.message, getDeadLetterQueue(), false); + // + } + } + else + { + _log.warn("Requested requeue of message:" + deliveryTag + " but no such delivery tag exists." + + _unacknowledgedMessageMap.size()); + + if (_log.isDebugEnabled()) + { + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + { + int count = 0; + + public boolean callback(UnacknowledgedMessage message) throws AMQException + { + _log.debug( + (count++) + ": (" + message.message.debugIdentity() + ")" + "[" + message.deliveryTag + "]"); + + return false; // Continue + } + + public void visitComplete() + { } + }); + } + } + + } + + /** + * 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 List msgToRequeue = new LinkedList(); + final List msgToResend = new LinkedList(); + + 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 UnacknowledgedMessageMap.Visitor() + { + public boolean callback(UnacknowledgedMessage message) throws AMQException + { + AMQShortString consumerTag = message.consumerTag; + AMQMessage msg = message.message; + msg.setRedelivered(true); + if (consumerTag != null) + { + // Consumer exists + if (_consumerTag2QueueMap.containsKey(consumerTag)) + { + msgToResend.add(message); + } + else // consumer has gone + { + msgToRequeue.add(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.queue != null) + { + if (requeue) + { + msgToRequeue.add(message); + } + else + { + _log.info("No DeadLetter Queue and requeue not requested so dropping message:" + message); + } + } + else + { + _log.info("Message.queue is null and no DeadLetter Queue so dropping message:" + message); + } + } + + // false means continue processing + return false; + } + + public void visitComplete() + { } + }); + + // 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 (UnacknowledgedMessage message : msgToResend) + { + AMQMessage msg = message.message; + + // 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 + msg.release(message.queue); + + // 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 = msg.getDeliveredSubscription(message.queue); + + if (sub != null) + { + // Get the lock so we can tell if the sub scription has closed. + // will stop delivery to this subscription until the lock is released. + // note: this approach would allow the use of a single queue if the + // PreDeliveryQueue would allow head additions. + // In the Java Qpid client we are suspended whilst doing this so it is all rather Mute.. + // needs guidance from AMQP WG Model SIG + synchronized (sub.getSendLock()) + { + if (sub.isClosed()) + { + if (_log.isDebugEnabled()) + { + _log.debug("Subscription(" + System.identityHashCode(sub) + + ") closed during resend so requeuing message"); + } + // move this message to requeue + msgToRequeue.add(message); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("Requeuing " + msg.debugIdentity() + " for resend via sub:" + + System.identityHashCode(sub)); + } + + sub.addToResendQueue(msg); + _unacknowledgedMessageMap.remove(message.deliveryTag); + } + } // sync(sub.getSendLock) + } + else + { + + if (_log.isInfoEnabled()) + { + _log.info("DeliveredSubscription not recorded so just requeueing(" + message.toString() + + ")to prevent loss"); + } + // move this message to requeue + msgToRequeue.add(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)) + { + if (_nonTransactedContext == null) + { + _nonTransactedContext = + new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks); + } + + deliveryContext = _nonTransactedContext; + } + else + { + deliveryContext = _txnContext; + } + + // Process Messages to Requeue at the front of the queue + for (UnacknowledgedMessage message : msgToRequeue) + { + message.message.release(message.queue); + message.message.setRedelivered(true); + + deliveryContext.deliver(message.message, message.queue, true); + + _unacknowledgedMessageMap.remove(message.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 + * + * @throws org.apache.qpid.AMQException if there is an error processing the unacked messages + */ + public void queueDeleted(final AMQQueue queue) throws AMQException + { + _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor() + { + public boolean callback(UnacknowledgedMessage message) throws AMQException + { + if (message.queue == queue) + { + try + { + message.discard(_storeContext); + message.queue = null; + } + catch (AMQException e) + { + _log.error( + "Error decrementing ref count on message " + message.message.getMessageId() + ": " + e, e); + } + } + + return false; + } + + public void visitComplete() + { } + }); + } + + /** + * 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 + { + synchronized (_unacknowledgedMessageMap.getLock()) + { + if (_log.isDebugEnabled()) + { + _log.debug("Unacked (PreAck) Size:" + _unacknowledgedMessageMap.size()); + } + + _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext); + + if (_log.isDebugEnabled()) + { + _log.debug("Unacked (PostAck) Size:" + _unacknowledgedMessageMap.size()); + } + + } + + checkSuspension(); + } + + /** + * Used only for testing purposes. + * + * @return the map of unacknowledged messages + */ + public UnacknowledgedMessageMap getUnacknowledgedMessageMap() + { + return _unacknowledgedMessageMap; + } + + private void checkSuspension() + { + boolean suspend; + + suspend = + ((_prefetch_HighWaterMark != 0) && (_unacknowledgedMessageMap.size() >= _prefetch_HighWaterMark)) + || ((_prefetchSize != 0) && (_prefetchSize < _unacknowledgedMessageMap.getUnacknowledgeBytes())); + + setSuspended(suspend); + } + + public void setSuspended(boolean suspended) + { + boolean isSuspended = _suspended.get(); + + if (isSuspended && !suspended) + { + // Continue being suspended if we are above the _prefetch_LowWaterMark + suspended = _unacknowledgedMessageMap.size() > _prefetch_LowWaterMark; + } + + boolean wasSuspended = _suspended.getAndSet(suspended); + if (wasSuspended != suspended) + { + if (wasSuspended) + { + _log.debug("Unsuspending channel " + this); + // may need to deliver queued messages + for (AMQQueue q : _consumerTag2QueueMap.values()) + { + q.deliverAsync(); + } + } + else + { + _log.debug("Suspending channel " + this); + } + } + } + + 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() + { + StringBuilder sb = new StringBuilder(30); + sb.append("Channel: id ").append(_channelId).append(", transaction mode: ").append(isTransactional()); + sb.append(", prefetch marks: ").append(_prefetch_LowWaterMark); + sb.append("/").append(_prefetch_HighWaterMark); + + return sb.toString(); + } + + public void setDefaultQueue(AMQQueue queue) + { + _defaultQueue = queue; + } + + public AMQQueue getDefaultQueue() + { + return _defaultQueue; + } + + public StoreContext getStoreContext() + { + return _storeContext; + } + + public void processReturns(AMQProtocolSession session) throws AMQException + { + 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 boolean wouldSuspend(AMQMessage msg) + { + if (isSuspended()) + { + return true; + } + else + { + boolean willSuspend = + ((_prefetch_HighWaterMark != 0) && ((_unacknowledgedMessageMap.size() + 1) > _prefetch_HighWaterMark)); + if (!willSuspend) + { + final long unackedSize = _unacknowledgedMessageMap.getUnacknowledgeBytes(); + + willSuspend = (_prefetchSize != 0) && (unackedSize != 0) && (_prefetchSize < (msg.getSize() + unackedSize)); + } + + if (willSuspend) + { + setSuspended(true); + } + + return willSuspend; + } + + } + + public TransactionalContext getTransactionalContext() + { + return _txnContext; + } + + public boolean isClosing() + { + return _closing; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ConsumerTagNotUniqueException.java new file mode 100644 index 0000000000..9a98af5689 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/Main.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/Main.java new file mode 100644 index 0000000000..a87c704cf8 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -0,0 +1,521 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.SimpleByteBufferAllocator; +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()); + + } + catch (ConfigurationException e) + { + System.out.println("Error configuring message broker: " + e); + _brokerLogger.error("Error configuring message broker: " + e); + e.printStackTrace(); + } + catch (Exception e) + { + System.out.println("Error intialising message broker: " + e); + _brokerLogger.error("Error intialising message broker: " + e); + e.printStackTrace(); + } + } + } + + 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 SimpleByteBufferAllocator()); + } + + 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); + } + + acceptor.bind(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 + { + + acceptor.bind(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()); + } + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ManagedChannel.java new file mode 100644 index 0000000000..e76f9c3f6c --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java new file mode 100644 index 0000000000..d61bb8916a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.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 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 final AMQMessage _amqMessage; + + public RequiredDeliveryException(String message, AMQMessage payload) + { + super(message); + + // 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(); + + // payload.incrementReference(); + } + + public AMQMessage getAMQMessage() + { + return _amqMessage; + } + + public AMQConstant getErrorCode() + { + return getReplyCode(); + } + + public abstract AMQConstant getReplyCode(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java new file mode 100644 index 0000000000..5ca8d57f7c --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.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.ack; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.txn.TxnOp; + +/** + * A TxnOp implementation for handling accumulated acks + */ +public class TxAck implements TxnOp +{ + private final UnacknowledgedMessageMap _map; + private final List _unacked = new LinkedList(); + private final List _individual = new LinkedList(); + private long _deliveryTag; + private boolean _multiple; + + public TxAck(UnacknowledgedMessageMap map) + { + _map = map; + } + + public void update(long deliveryTag, boolean multiple) + { + if (!multiple) + { + //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() + { + //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); + } + //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 + { + //if any of the messages in unacked are persistent the txn + //buffer must be marked as persistent: + for (UnacknowledgedMessage msg : _unacked) + { + if (msg.message.isPersistent()) + { + return true; + } + } + return false; + } + + public void prepare(StoreContext storeContext) throws AMQException + { + //make persistent changes, i.e. dequeue and decrementReference + for (UnacknowledgedMessage msg : _unacked) + { + msg.restoreTransientMessageData(); + + //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 (UnacknowledgedMessage msg : _unacked) + { + msg.clearTransientMessageData(); + msg.message.takeReference(); + } + } + + public void commit(StoreContext storeContext) + { + //remove the unacked messages from the channels map + _map.remove(_unacked); + for (UnacknowledgedMessage msg : _unacked) + { + msg.clearTransientMessageData(); + } + + } + + public void rollback(StoreContext storeContext) + { + } +} + + diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java new file mode 100644 index 0000000000..7088c704ed --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.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.ack; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.StoreContext; + +public class UnacknowledgedMessage +{ + public final AMQMessage message; + public final AMQShortString consumerTag; + public final long deliveryTag; + public AMQQueue queue; + + public UnacknowledgedMessage(AMQQueue queue, AMQMessage message, AMQShortString consumerTag, long deliveryTag) + { + this.queue = queue; + this.message = message; + this.consumerTag = consumerTag; + this.deliveryTag = deliveryTag; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append("Q:"); + sb.append(queue); + sb.append(" M:"); + sb.append(message); + sb.append(" CT:"); + sb.append(consumerTag); + sb.append(" DT:"); + sb.append(deliveryTag); + + return sb.toString(); + } + + public void discard(StoreContext storeContext) throws AMQException + { + if (queue != null) + { + queue.dequeue(storeContext, message); + } + //if the queue is null then the message is waiting to be acked, but has been removed. + message.decrementReference(storeContext); + } + + public void restoreTransientMessageData() throws AMQException + { + message.restoreTransientMessageData(); + } + + public void clearTransientMessageData() + { + message.clearTransientMessageData(); + } +} + diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java new file mode 100644 index 0000000000..b69a917081 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.ack; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.txn.TransactionalContext; + +public interface UnacknowledgedMessageMap +{ + public interface Visitor + { + /** + * @param message the message being iterated over + * @return true to stop iteration, false to continue + * @throws AMQException + */ + boolean callback(UnacknowledgedMessage message) throws AMQException; + + void visitComplete(); + } + + void visit(Visitor visitor) throws AMQException; + + Object getLock(); + + void add(long deliveryTag, UnacknowledgedMessage message); + + void collect(long deliveryTag, boolean multiple, List msgs); + + boolean contains(long deliveryTag) throws AMQException; + + void remove(List msgs); + + UnacknowledgedMessage remove(long deliveryTag); + + void drainTo(Collection destination, long deliveryTag) throws AMQException; + + Collection cancelAllMessages(); + + void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) throws AMQException; + + int size(); + + void clear(); + + UnacknowledgedMessage 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/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java new file mode 100644 index 0000000000..30bbdea2ef --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.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.ack; + +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.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 UnacknowledgedMessageMapImpl(Object lock, Map map) + { + _lock = lock; + _map = map; + } */ + + public void collect(long deliveryTag, boolean multiple, List msgs) + { + if (multiple) + { + collect(deliveryTag, msgs); + } + else + { + msgs.add(get(deliveryTag)); + } + + } + + public boolean contains(long deliveryTag) throws AMQException + { + synchronized (_lock) + { + return _map.containsKey(deliveryTag); + } + } + + public void remove(List msgs) + { + synchronized (_lock) + { + for (UnacknowledgedMessage msg : msgs) + { + remove(msg.deliveryTag); + } + } + } + + public UnacknowledgedMessage remove(long deliveryTag) + { + synchronized (_lock) + { + + UnacknowledgedMessage message = _map.remove(deliveryTag); + if(message != null) + { + _unackedSize -= message.message.getSize(); + } + + return message; + } + } + + public void visit(Visitor visitor) throws AMQException + { + synchronized (_lock) + { + Collection currentEntries = _map.values(); + for (UnacknowledgedMessage msg : currentEntries) + { + visitor.callback(msg); + } + visitor.visitComplete(); + } + } + + public Object getLock() + { + return _lock; + } + + public void add(long deliveryTag, UnacknowledgedMessage message) + { + synchronized (_lock) + { + _map.put(deliveryTag, message); + _unackedSize += message.message.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(Collection destination, long deliveryTag) 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()); + } + + it.remove(); + _unackedSize -= unacked.getValue().message.getSize(); + + destination.add(unacked.getValue()); + if (unacked.getKey() == deliveryTag) + { + break; + } + } + } + } + + public UnacknowledgedMessage get(long key) + { + synchronized (_lock) + { + return _map.get(key); + } + } + + public Set getDeliveryTags() + { + synchronized (_lock) + { + return _map.keySet(); + } + } + + private void collect(long key, List msgs) + { + synchronized (_lock) + { + for (Map.Entry entry : _map.entrySet()) + { + msgs.add(entry.getValue()); + if (entry.getKey() == key) + { + break; + } + } + } + } + + public long getUnacknowledgeBytes() + { + return _unackedSize; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/Configurator.java new file mode 100644 index 0000000000..31c1b61a21 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java new file mode 100644 index 0000000000..8573902af4 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.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.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.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.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(AMQQueue queue) + { + CompositeConfiguration queueConfiguration = null; + if (_config == null) + return null; + + Configuration vHostConfiguration = _config.subset(VIRTUALHOST_PROPERTY_BASE + queue.getVirtualHost().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); + + queue = new AMQQueue(queueName, + durable, + owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */, + autodelete /* Therefore autodelete makes no sence */, virtualHost); + + 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(routingKey, null, exchange); + + + _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + exchangeName + " RK:'" + routingKey + "'"); + } + + if(exchange != virtualHost.getExchangeRegistry().getDefaultExchange()) + { + queue.bind(queue.getName(), null, virtualHost.getExchangeRegistry().getDefaultExchange()); + } + } + + } + + + + Configurator.configure(queue, queueConfiguration); + } + + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java new file mode 100644 index 0000000000..9ebb893362 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -0,0 +1,217 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.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(); + } + } + + abstract public Map> getBindings(); + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java new file mode 100644 index 0000000000..c349b44d6d --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.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.server.exchange; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +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; + _exchangeClassMap.put(ExchangeDefaults.DIRECT_EXCHANGE_CLASS, org.apache.qpid.server.exchange.DestNameExchange.class); + _exchangeClassMap.put(ExchangeDefaults.TOPIC_EXCHANGE_CLASS, org.apache.qpid.server.exchange.DestWildExchange.class); + _exchangeClassMap.put(ExchangeDefaults.HEADERS_EXCHANGE_CLASS, org.apache.qpid.server.exchange.HeadersExchange.class); + _exchangeClassMap.put(ExchangeDefaults.FANOUT_EXCHANGE_CLASS, org.apache.qpid.server.exchange.FanoutExchange.class); + + } + + public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, + int ticket) + throws AMQException + { + Class exchClass = _exchangeClassMap.get(type); + if (exchClass == null) + { + + throw new AMQUnknownExchangeType("Unknown exchange type: " + type); + } + try + { + Exchange e = exchClass.newInstance(); + e.initialise(_host, exchange, durable, ticket, autoDelete); + return e; + } + catch (InstantiationException e) + { + throw new AMQException("Unable to create exchange: " + e, e); + } + catch (IllegalAccessException e) + { + throw new AMQException("Unable to create exchange: " + e, e); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java new file mode 100644 index 0000000000..98abf7977a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.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.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.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(AMQMessage payload) throws AMQException + { + final AMQShortString exchange = payload.getMessagePublishInfo().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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java new file mode 100644 index 0000000000..5edffc19ed --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.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.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; + +public class DestNameExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(DestNameExchange.class); + + /** + * Maps from queue name to queue instances + */ + private final Index _index = new Index(); + + /** + * MBean class implementing the management interfaces. + */ + @MBeanDescription("Management Bean for Direct Exchange") + private final class DestNameExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ direct exchange") + public DestNameExchangeMBean() 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(new AMQShortString(binding), null, DestNameExchange.this); + } + catch (AMQException ex) + { + throw new MBeanException(ex); + } + } + + }// End of MBean class + + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new DestNameExchangeMBean(); + } + 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)) + { + _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey); + } + else + { + _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; + assert routingKey != null; + + if (!_index.remove(routingKey, queue)) + { + throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() + + " with routing key " + routingKey + ". No queue was registered with that _routing key"); + } + } + + public void route(AMQMessage payload) throws AMQException + { + final MessagePublishInfo info = payload.getMessagePublishInfo(); + final AMQShortString routingKey = info.getRoutingKey(); + final List queues = (routingKey == null) ? null : _index.get(routingKey); + if (queues == null || queues.isEmpty()) + { + String msg = "Routing key " + routingKey + " is not known to " + this; + if (info.isMandatory() || info.isImmediate()) + { + throw new NoRouteException(msg, payload); + } + else + { + _logger.error("MESSAGE LOSS: Message should be sent on a Dead Letter Queue"); + _logger.warn(msg); + } + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + queues); + } + + for (AMQQueue q : queues) + { + payload.enqueue(q); + } + } + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java new file mode 100644 index 0000000000..b55dbcc792 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java @@ -0,0 +1,426 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; + +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.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +public class DestWildExchange extends AbstractExchange +{ + private static final Logger _logger = Logger.getLogger(DestWildExchange.class); + + private ConcurrentHashMap> _routingKey2queues = + new ConcurrentHashMap>(); + // private ConcurrentHashMap _routingKey2queue = new ConcurrentHashMap(); + private static final String TOPIC_SEPARATOR = "."; + private static final String AMQP_STAR = "*"; + private static final String AMQP_HASH = "#"; + + /** DestWildExchangeMBean class implements the management interface for the Topic exchanges. */ + @MBeanDescription("Management Bean for Topic Exchange") + private final class DestWildExchangeMBean extends ExchangeMBean + { + @MBeanConstructor("Creates an MBean for AMQ topic exchange") + public DestWildExchangeMBean() throws JMException + { + super(); + _exchangeType = "topic"; + init(); + } + + /** returns exchange bindings in tabular form */ + public TabularData bindings() throws OpenDataException + { + _bindingList = new TabularDataSupport(_bindinglistDataType); + for (Map.Entry> entry : _routingKey2queues.entrySet()) + { + AMQShortString key = entry.getKey(); + List queueList = new ArrayList(); + + List queues = getMatchedQueues(key); + 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(new AMQShortString(binding), null, DestWildExchange.this); + } + 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; + + AMQShortString routingKey = normalize(rKey); + + _logger.debug("Registering queue " + queue.getName() + " with routing key " + routingKey); + // we need to use putIfAbsent, which is an atomic operation, to avoid a race condition + List queueList = _routingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList()); + // if we got null back, no previous value was associated with the specified routing key hence + // we need to read back the new value just put into the map + if (queueList == null) + { + queueList = _routingKey2queues.get(routingKey); + } + + if (!queueList.contains(queue)) + { + queueList.add(queue); + } + else if (_logger.isDebugEnabled()) + { + _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey); + } + + } + + private AMQShortString normalize(AMQShortString routingKey) + { + StringTokenizer routingTokens = new StringTokenizer(routingKey.toString(), TOPIC_SEPARATOR); + List _subscription = new ArrayList(); + + while (routingTokens.hasMoreTokens()) + { + _subscription.add(routingTokens.nextToken()); + } + + int size = _subscription.size(); + + for (int index = 0; index < size; index++) + { + // if there are more levels + if ((index + 1) < size) + { + if (_subscription.get(index).equals(AMQP_HASH)) + { + if (_subscription.get(index + 1).equals(AMQP_HASH)) + { + // we don't need #.# delete this one + _subscription.remove(index); + size--; + // redo this normalisation + index--; + } + + if (_subscription.get(index + 1).equals(AMQP_STAR)) + { + // we don't want #.* swap to *.# + // remove it and put it in at index + 1 + _subscription.add(index + 1, _subscription.remove(index)); + } + } + } // if we have more levels + } + + StringBuilder sb = new StringBuilder(); + + for (String s : _subscription) + { + sb.append(s); + sb.append(TOPIC_SEPARATOR); + } + + sb.deleteCharAt(sb.length() - 1); + + return new AMQShortString(sb.toString()); + } + + public void route(AMQMessage payload) throws AMQException + { + MessagePublishInfo info = payload.getMessagePublishInfo(); + + final AMQShortString routingKey = normalize(info.getRoutingKey()); + + List queues = getMatchedQueues(routingKey); + // if we have no registered queues we have nothing to do + // TODO: add support for the immediate flag + if ((queues == null) || queues.isEmpty()) + { + if (info.isMandatory() || info.isImmediate()) + { + String msg = "Topic " + routingKey + " is not known to " + this; + throw new NoRouteException(msg, payload); + } + else + { + _logger.warn("No queues found for routing key " + routingKey); + _logger.warn("Routing map contains: " + _routingKey2queues); + + return; + } + } + + for (AMQQueue q : queues) + { + // TODO: modify code generator to add clone() method then clone the deliver body + // without this addition we have a race condition - we will be modifying the body + // before the encoder has encoded the body for delivery + payload.enqueue(q); + } + } + + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) + { + return isBound(routingKey, queue); + } + + public boolean isBound(AMQShortString routingKey, AMQQueue queue) + { + List queues = _routingKey2queues.get(normalize(routingKey)); + + return (queues != null) && queues.contains(queue); + } + + public boolean isBound(AMQShortString routingKey) + { + List queues = _routingKey2queues.get(normalize(routingKey)); + + return (queues != null) && !queues.isEmpty(); + } + + public boolean isBound(AMQQueue queue) + { + for (List queues : _routingKey2queues.values()) + { + if (queues.contains(queue)) + { + return true; + } + } + + return false; + } + + public boolean hasBindings() + { + return !_routingKey2queues.isEmpty(); + } + + public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException + { + assert queue != null; + assert rKey != null; + + AMQShortString routingKey = normalize(rKey); + + List queues = _routingKey2queues.get(routingKey); + if (queues == null) + { + throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() + + " with routing key " + routingKey + ". No queue was registered with that _routing key"); + + } + + boolean removedQ = queues.remove(queue); + if (!removedQ) + { + throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() + + " with routing key " + routingKey); + } + + if (queues.isEmpty()) + { + _routingKey2queues.remove(routingKey); + } + } + + protected ExchangeMBean createMBean() throws AMQException + { + try + { + return new DestWildExchangeMBean(); + } + 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); + } + } + + public Map> getBindings() + { + return _routingKey2queues; + } + + private List getMatchedQueues(AMQShortString routingKey) + { + List list = new LinkedList(); + StringTokenizer routingTokens = new StringTokenizer(routingKey.toString(), TOPIC_SEPARATOR); + + ArrayList routingkeyList = new ArrayList(); + + while (routingTokens.hasMoreTokens()) + { + String next = routingTokens.nextToken(); + if (next.equals(AMQP_HASH) && routingkeyList.get(routingkeyList.size() - 1).equals(AMQP_HASH)) + { + continue; + } + + routingkeyList.add(next); + } + + for (AMQShortString queue : _routingKey2queues.keySet()) + { + StringTokenizer queTok = new StringTokenizer(queue.toString(), TOPIC_SEPARATOR); + + ArrayList queueList = new ArrayList(); + + while (queTok.hasMoreTokens()) + { + queueList.add(queTok.nextToken()); + } + + int depth = 0; + boolean matching = true; + boolean done = false; + int routingskip = 0; + int queueskip = 0; + + while (matching && !done) + { + if ((queueList.size() == (depth + queueskip)) || (routingkeyList.size() == (depth + routingskip))) + { + done = true; + + // if it was the routing key that ran out of digits + if (routingkeyList.size() == (depth + routingskip)) + { + if (queueList.size() > (depth + queueskip)) + { // a hash and it is the last entry + matching = + queueList.get(depth + queueskip).equals(AMQP_HASH) + && (queueList.size() == (depth + queueskip + 1)); + } + } + else if (routingkeyList.size() > (depth + routingskip)) + { + // There is still more routing key to check + matching = false; + } + + continue; + } + + // if the values on the two topics don't match + if (!queueList.get(depth + queueskip).equals(routingkeyList.get(depth + routingskip))) + { + if (queueList.get(depth + queueskip).equals(AMQP_STAR)) + { + depth++; + + continue; + } + else if (queueList.get(depth + queueskip).equals(AMQP_HASH)) + { + // Is this a # at the end + if (queueList.size() == (depth + queueskip + 1)) + { + done = true; + + continue; + } + + // otherwise # in the middle + while (routingkeyList.size() > (depth + routingskip)) + { + if (routingkeyList.get(depth + routingskip).equals(queueList.get(depth + queueskip + 1))) + { + queueskip++; + depth++; + + break; + } + + routingskip++; + } + + continue; + } + + matching = false; + } + + depth++; + } + + if (matching) + { + list.addAll(_routingKey2queues.get(queue)); + } + } + + return list; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java new file mode 100644 index 0000000000..37cd85a8f8 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.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.exchange; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.queue.AMQMessage; +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(AMQMessage 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(); + + Map> getBindings(); + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java new file mode 100644 index 0000000000..e07fd0b8fc --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +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; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInUseException.java new file mode 100644 index 0000000000..c77f114428 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java new file mode 100644 index 0000000000..fe3b19e74e --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java new file mode 100644 index 0000000000..aa13f1d8ee --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.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.exchange; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.management.MBeanConstructor; +import org.apache.qpid.server.management.MBeanDescription; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; + +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.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(new AMQShortString(binding), null, FanoutExchange.this); + } + 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 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("Queue " + queue + " was not registered with exchange " + this.getName() + ". "); + } + } + + public void route(AMQMessage payload) throws AMQException + { + final MessagePublishInfo publishInfo = payload.getMessagePublishInfo(); + final AMQShortString routingKey = publishInfo.getRoutingKey(); + if ((_queues == null) || _queues.isEmpty()) + { + String msg = "No queues bound to " + this; + if (publishInfo.isMandatory() || publishInfo.isImmediate()) + { + throw new NoRouteException(msg, payload); + } + else + { + _logger.warn(msg); + } + } + else + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Publishing message to queue " + _queues); + } + + for (AMQQueue q : _queues) + { + payload.enqueue(q); + } + } + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java new file mode 100644 index 0000000000..2b7df4361a --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java new file mode 100644 index 0000000000..3544e9d1f8 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -0,0 +1,323 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.exchange; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +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.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; + +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.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); + + 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()); + _bindings.remove(new Registration(new HeadersBinding(args), queue)); + } + + public void route(AMQMessage payload) throws AMQException + { + FieldTable headers = getHeaders(payload.getContentHeaderBody()); + if (_logger.isDebugEnabled()) + { + _logger.debug("Exchange " + getName() + ": routing message with headers " + headers); + } + boolean routed = false; + 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()); + } + payload.enqueue(e.queue); + routed = true; + } + } + if (!routed) + { + + String msg = "Exchange " + getName() + ": message not routable."; + + if (payload.getMessagePublishInfo().isMandatory() || payload.getMessagePublishInfo().isImmediate()) + { + throw new NoRouteException(msg, payload); + } + else + { + _logger.warn(msg); + } + + } + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java new file mode 100644 index 0000000000..eacdad8a8e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.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.server.exchange; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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 DestNameExchange. + */ +class Index +{ + private ConcurrentMap> _index + = new ConcurrentHashMap>(); + + synchronized boolean add(AMQShortString key, AMQQueue queue) + { + List queues = _index.get(key); + if(queues == null) + { + queues = new CopyOnWriteArrayList(); + //next call is atomic, so there is no race to create the list + List active = _index.putIfAbsent(key, queues); + if(active != null) + { + //someone added the new one in faster than we did, so use theirs + queues = active; + } + } + if(queues.contains(queue)) + { + return false; + } + else + { + return queues.add(queue); + } + } + + synchronized boolean remove(AMQShortString key, AMQQueue queue) + { + List queues = _index.get(key); + if (queues != null) + { + boolean removed = queues.remove(queue); + if (queues.size() == 0) + { + _index.remove(key); + } + return removed; + } + return false; + } + + List get(AMQShortString key) + { + return _index.get(key); + } + + Map> getBindingsMap() + { + return new HashMap>(_index); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/ManagedExchange.java new file mode 100644 index 0000000000..5d6d68b3c8 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java new file mode 100644 index 0000000000..7508e80f7f --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.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 org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; + +/** + * 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(AMQMessage message) throws AMQException; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java new file mode 100644 index 0000000000..1d6ab3842d --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.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.exchange; + +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.queue.AMQMessage; + +/** + * 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 message) + { + super(msg, message); + } + + public AMQConstant getReplyCode() + { + return AMQConstant.NO_ROUTE; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java new file mode 100644 index 0000000000..fb5220f4da --- /dev/null +++ b/Final2/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.AMQMessage; + +/** + * 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(AMQMessage message) throws AMQException + { + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java new file mode 100644 index 0000000000..024257bea9 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java new file mode 100644 index 0000000000..e28ff79820 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.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.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; + +/** + * 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 AMQException + */ + public boolean matches(AMQMessage message) throws AMQException; + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java new file mode 100644 index 0000000000..72a9ef7969 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.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.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; + +/** + * 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(AMQMessage message) throws AMQException + { + + 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(AMQMessage message) throws AMQException + { + 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 ComparisonExpression(left, right) + { + + public Object evaluate(AMQMessage message) throws AMQException + { + 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) + { + 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(AMQMessage message) throws AMQException + { + 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(AMQMessage message) throws AMQException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java new file mode 100644 index 0000000000..0e729cc521 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java @@ -0,0 +1,210 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.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; + +/** + * 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(AMQMessage message) throws AMQException + { + 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(AMQMessage message) throws AMQException + { + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java new file mode 100644 index 0000000000..5f646c15db --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.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.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; + +/** + * Represents an expression + */ +public interface Expression +{ + + /** + * @return the value of this expression + */ + public Object evaluate(AMQMessage message) throws AMQException; + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java new file mode 100644 index 0000000000..c82de9fa15 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.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.filter; +// +// Based on like named file from r450141 of the Apache ActiveMQ project +// + +import org.apache.qpid.server.queue.AMQMessage; + +public interface FilterManager +{ + void add(MessageFilter filter); + + void remove(MessageFilter filter); + + boolean allAllow(AMQMessage msg); + + boolean hasFilters(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java new file mode 100644 index 0000000000..311f0680ec --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.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 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) + { + + manager = new SimpleFilterManager(); + + if(filters.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue())) + { + String selector = filters.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); + + if (selector != null && !selector.equals("")) + { + manager.add(new JMSSelectorFilter(selector)); + } + + } + + if (filters.containsKey(AMQPFilterTypes.NO_CONSUME.getValue())) + { + manager.add(new NoConsumerFilter()); + } + + + + //If we added no filters don't bear the overhead of having an filter manager + if (!manager.hasFilters()) + { + manager = null; + } + } + else + { + _logger.debug("No Filters found."); + } + + + return manager; + + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java new file mode 100644 index 0000000000..2061803d65 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.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.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.AMQMessage; + + + +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; + _logger.info("Created JMSSelectorFilter with selector:" + _selector); + + + _matcher = new SelectorParser().parse(selector); + + + } + + public boolean matches(AMQMessage message) + { + try + { + boolean match = _matcher.matches(message); + _logger.info(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector); + return match; + } + catch (AMQException e) + { + //fixme this needs to be sorted.. it shouldn't happen + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + return false; + } + + public String getSelector() + { + return _selector; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java new file mode 100644 index 0000000000..c8cbdb2125 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.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.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; + +/** + * 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(AMQMessage message) throws AMQException + { + + 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(AMQMessage message) throws AMQException + { + + 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(AMQMessage message) throws AMQException; + + public boolean matches(AMQMessage message) throws AMQException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java new file mode 100644 index 0000000000..e6bfe974d5 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.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.filter; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; + +public interface MessageFilter +{ + boolean matches(AMQMessage message) throws AMQException; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java new file mode 100644 index 0000000000..47ca930d12 --- /dev/null +++ b/Final2/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.AMQMessage; + +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(AMQMessage message) + { + return true; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java new file mode 100644 index 0000000000..5ab360ca19 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java @@ -0,0 +1,322 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.AMQMessage; + +/** + * 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(); + + static + { + JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression() + { + public Object evaluate(AMQMessage message) + { + //TODO + return null; + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new Expression() + { + public Object evaluate(AMQMessage message) + { + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString replyTo = _properties.getReplyTo(); + + return (replyTo == null) ? null : replyTo.toString(); + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new Expression() + { + public Object evaluate(AMQMessage message) + { + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString type = _properties.getType(); + + return (type == null) ? null : type.toString(); + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new Expression() + { + public Object evaluate(AMQMessage message) + { + try + { + int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT; + if (_logger.isDebugEnabled()) + { + _logger.debug("JMSDeliveryMode is :" + mode); + } + + return mode; + } + catch (AMQException e) + { + _logger.warn(e); + } + + return NON_PERSISTENT; + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new Expression() + { + public Object evaluate(AMQMessage message) + { + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return (int) _properties.getPriority(); + } + catch (AMQException e) + { + _logger.warn(e); + } + + return DEFAULT_PRIORITY; + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new Expression() + { + public Object evaluate(AMQMessage message) + { + + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString messageId = _properties.getMessageId(); + + return (messageId == null) ? null : messageId; + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression() + { + public Object evaluate(AMQMessage message) + { + + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getTimestamp(); + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new Expression() + { + public Object evaluate(AMQMessage message) + { + + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + AMQShortString correlationId = _properties.getCorrelationId(); + + return (correlationId == null) ? null : correlationId.toString(); + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression() + { + public Object evaluate(AMQMessage message) + { + + try + { + CommonContentHeaderProperties _properties = + (CommonContentHeaderProperties) + message.getContentHeaderBody().properties; + + return _properties.getExpiration(); + } + catch (AMQException e) + { + _logger.warn(e); + + return null; + } + + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression() + { + public Object evaluate(AMQMessage message) + { + return message.isRedelivered(); + } + }); + + } + + private final String name; + private final Expression jmsPropertyExpression; + + public PropertyExpression(String name) + { + this.name = name; + jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name); + } + + public Object evaluate(AMQMessage message) throws AMQException + { + + 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); + + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java new file mode 100644 index 0000000000..62a45f5420 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.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.filter; + +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; + +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(AMQMessage 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/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java new file mode 100644 index 0000000000..83b4ed5358 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.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.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.AMQMessage; + +/** + * 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(AMQMessage message) throws AMQException + { + Object rvalue = right.evaluate(message); + if (rvalue == null) + { + return null; + } + + if (rvalue instanceof Number) + { + return 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(AMQMessage message) throws AMQException + { + + 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(AMQMessage message) throws AMQException + { + Object object = evaluate(message); + + return (object != null) && (object == Boolean.TRUE); + } + } + ; + + public static BooleanExpression createNOT(BooleanExpression left) + { + return new BooleanUnaryExpression(left) + { + public Object evaluate(AMQMessage message) throws AMQException + { + 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 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 BooleanUnaryExpression(left) + { + public Object evaluate(AMQMessage message) throws AMQException + { + 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 (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(); + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java new file mode 100644 index 0000000000..f5454afae5 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java @@ -0,0 +1,126 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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 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(AMQMessage 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(AMQMessage 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(AMQMessage message) throws AMQException + { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java new file mode 100644 index 0000000000..f5debb607a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.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.filter; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.queue.AMQMessage; + +// +// 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(AMQMessage 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(AMQMessage message) throws AMQException + { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java new file mode 100644 index 0000000000..35d770fd5d --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.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.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.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(AMQMessage 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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicAckMethodHandler.java new file mode 100644 index 0000000000..a6972475a6 --- /dev/null +++ b/Final2/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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession protocolSession = stateManager.getProtocolSession(); + BasicAckBody body = evt.getMethod(); + + if (_log.isDebugEnabled()) + { + _log.debug("Ack(Tag:" + body.deliveryTag + ":Mult:" + body.multiple + ") received on channel " + evt.getChannelId()); + } + + final AMQChannel channel = protocolSession.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + // this method throws an AMQException if the delivery tag is not known + channel.acknowledgeMessage(body.deliveryTag, body.multiple); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java new file mode 100644 index 0000000000..8bab96a11b --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.BasicCancelOkBody; +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; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession protocolSession = stateManager.getProtocolSession(); + + final AMQChannel channel = protocolSession.getChannel(evt.getChannelId()); + final BasicCancelBody body = evt.getMethod(); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + if (_log.isDebugEnabled()) + { + _log.debug("BasicCancel: for:" + body.consumerTag + + " nowait:" + body.nowait); + } + + channel.unsubscribeConsumer(protocolSession, body.consumerTag); + if (!body.nowait) + { + // 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. + final AMQFrame responseFrame = BasicCancelOkBody.createAMQFrame(evt.getChannelId(), + (byte) 8, (byte) 0, // AMQP version (major, minor) + body.consumerTag); // consumerTag + protocolSession.writeFrame(responseFrame); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java new file mode 100644 index 0000000000..d1e4ae6aa6 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.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.server.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicConsumeOkBody; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +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.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 _log = Logger.getLogger(BasicConsumeMethodHandler.class); + + private static final BasicConsumeMethodHandler _instance = new BasicConsumeMethodHandler(); + + public static BasicConsumeMethodHandler getInstance() + { + return _instance; + } + + private BasicConsumeMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + BasicConsumeBody body = evt.getMethod(); + final int channelId = evt.getChannelId(); + + AMQChannel channel = session.getChannel(channelId); + + VirtualHost vHost = session.getVirtualHost(); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + else + { + if (_log.isDebugEnabled()) + { + _log.debug("BasicConsume: from '" + body.queue + + "' for:" + body.consumerTag + + " nowait:" + body.nowait + + " args:" + body.arguments); + } + + AMQQueue queue = body.queue == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.queue); + + if (queue == null) + { + if (_log.isTraceEnabled()) + { + _log.trace("No queue for '" + body.queue + "'"); + } + if (body.queue != null) + { + String msg = "No such queue, '" + body.queue + "'"; + 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 + { + + if (body.consumerTag != null) + { + body.consumerTag = body.consumerTag.intern(); + } + + try + { + AMQShortString consumerTag = channel.subscribeToQueue(body.consumerTag, queue, session, !body.noAck, + body.arguments, body.noLocal, body.exclusive); + if (!body.nowait) + { + // 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(BasicConsumeOkBody.createAMQFrame(channelId, + (byte) 8, (byte) 0, // AMQP version (major, minor) + consumerTag)); // consumerTag + } + + //now allow queue to start async processing of any backlog of messages + queue.deliverAsync(); + } + catch (org.apache.qpid.AMQInvalidArgumentException ise) + { + _log.debug("Closing connection due to invalid selector"); + // Why doesn't this ChannelException work. +// throw body.getChannelException(AMQConstant.INVALID_ARGUMENT, ise.getMessage()); + // 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(ChannelCloseBody.createAMQFrame(channelId, + (byte) 8, (byte) 0, // AMQP version (major, minor) + BasicConsumeBody.getClazz((byte) 8, (byte) 0), // classId + BasicConsumeBody.getMethod((byte) 8, (byte) 0), // methodId + AMQConstant.INVALID_ARGUMENT.getCode(), // replyCode + new AMQShortString(ise.getMessage()))); // replyText + } + catch (ConsumerTagNotUniqueException e) + { + AMQShortString msg = new AMQShortString("Non-unique consumer tag, '" + body.consumerTag + "'"); + // If the above doesn't work then perhaps this is wrong too. +// throw body.getConnectionException(AMQConstant.NOT_ALLOWED, +// "Non-unique consumer tag, '" + body.consumerTag + "'"); + // 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(ConnectionCloseBody.createAMQFrame(channelId, + (byte) 8, (byte) 0, // AMQP version (major, minor) + BasicConsumeBody.getClazz((byte) 8, (byte) 0), // classId + BasicConsumeBody.getMethod((byte) 8, (byte) 0), // methodId + AMQConstant.NOT_ALLOWED.getCode(), // replyCode + msg)); // replyText + } + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java new file mode 100644 index 0000000000..782a89c704 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.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.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.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +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; + +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + BasicGetBody body = evt.getMethod(); + final int channelId = evt.getChannelId(); + VirtualHost vHost = session.getVirtualHost(); + + AMQChannel channel = session.getChannel(channelId); + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + else + { + AMQQueue queue = body.queue == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.queue); + + if (queue == null) + { + _log.info("No queue for '" + body.queue + "'"); + if(body.queue!=null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, + "No such queue, '" + body.queue + "'"); + } + else + { + throw body.getConnectionException(AMQConstant.NOT_ALLOWED, + "No queue name provided, no default queue defined."); + } + } + else + { + if(!queue.performGet(session, channel, !body.noAck)) + { + + + // TODO - set clusterId + session.writeFrame(BasicGetEmptyBody.createAMQFrame(channelId, body.getMajor(), body.getMinor(), null)); + } + } + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java new file mode 100644 index 0000000000..68d4483df3 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.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.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.protocol.AMQMethodEvent; +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.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 _log = Logger.getLogger(BasicPublishMethodHandler.class); + + private static final BasicPublishMethodHandler _instance = new BasicPublishMethodHandler(); + + + public static BasicPublishMethodHandler getInstance() + { + return _instance; + } + + private BasicPublishMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + final BasicPublishBody body = evt.getMethod(); + + if (_log.isDebugEnabled()) + { + _log.debug("Publish received on channel " + evt.getChannelId()); + } + + // TODO: check the delivery tag field details - is it unique across the broker or per subscriber? + if (body.exchange == null) + { + body.exchange = ExchangeDefaults.DEFAULT_EXCHANGE_NAME; + + } + else + { + body.exchange = body.exchange.intern(); + } + VirtualHost vHost = session.getVirtualHost(); + Exchange e = vHost.getExchangeRegistry().getExchange(body.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(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + if(body.routingKey != null) + { + body.routingKey = body.routingKey.intern(); + } + + MessagePublishInfo info = session.getRegistry().getProtocolVersionMethodConverter().convertToInfo(body); + channel.setPublishFrame(info, session); + } + } +} + + + diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java new file mode 100644 index 0000000000..3cd6a87f64 --- /dev/null +++ b/Final2/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.BasicQosOkBody; +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 BasicQosHandler implements StateAwareMethodListener +{ + private static final BasicQosHandler _instance = new BasicQosHandler(); + + public static BasicQosHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + AMQChannel channel = session.getChannel(evt.getChannelId()); + if (channel == null) + { + throw evt.getMethod().getChannelNotFoundException(evt.getChannelId()); + } + + channel.setPrefetchCount(evt.getMethod().prefetchCount); + channel.setPrefetchSize(evt.getMethod().prefetchSize); + + // 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(BasicQosOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0)); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java new file mode 100644 index 0000000000..a436c35473 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRecoverMethodHandler.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.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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + _logger.debug("Recover received on protocol session " + session + " and channel " + evt.getChannelId()); + AMQChannel channel = session.getChannel(evt.getChannelId()); + BasicRecoverBody body = evt.getMethod(); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + channel.resend(body.requeue); + + // 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(BasicRecoverOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0)); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java new file mode 100644 index 0000000000..aca485f62a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java @@ -0,0 +1,130 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.protocol.AMQMethodEvent; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.ack.UnacknowledgedMessage; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + int channelId = evt.getChannelId(); + +// if (_logger.isDebugEnabled()) +// { +// _logger.debug("Rejecting:" + evt.getMethod().deliveryTag + +// ": Requeue:" + evt.getMethod().requeue + +//// ": Resend:" + evt.getMethod().resend + +// " on channel:" + channelId); +// } + + AMQChannel channel = session.getChannel(channelId); + + if (channel == null) + { + throw evt.getMethod().getChannelNotFoundException(channelId); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting:" + evt.getMethod().deliveryTag + + ": Requeue:" + evt.getMethod().requeue + + //": Resend:" + evt.getMethod().resend + + " on channel:" + channel.debugIdentity()); + } + + long deliveryTag = evt.getMethod().deliveryTag; + + UnacknowledgedMessage 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.queue == null || message.queue.isDeleted()) + { + _logger.warn("Message's Queue as already been purged, unable to Reject. " + + "Dropping message should use Dead Letter Queue"); + //sendtoDeadLetterQueue(msg) + return; + } + + if (!message.message.isReferenced()) + { + _logger.warn("Message as already been purged, unable to Reject."); + return; + } + + + if (_logger.isTraceEnabled()) + { + _logger.trace("Rejecting: DT:" + deliveryTag + "-" + message.message.debugIdentity() + + ": Requeue:" + evt.getMethod().requeue + + //": 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.message.reject(message.message.getDeliveredSubscription(message.queue)); + } + + if (evt.getMethod().requeue) + { + channel.requeue(deliveryTag); + } + else + { + _logger.warn("Dropping message as requeue not required and there is no dead letter queue"); + //sendtoDeadLetterQueue(AMQMessage message) +// message.queue = channel.getDefaultDeadLetterQueue(); +// channel.requeue(deliveryTag); + } + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.java new file mode 100644 index 0000000000..1f4f1f9221 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseHandler.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.AMQFrame; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + ChannelCloseBody body = evt.getMethod(); + if (_logger.isInfoEnabled()) + { + _logger.info("Received channel close for id " + evt.getChannelId() + " citing class " + body.classId + + " and method " + body.methodId); + } + int channelId = evt.getChannelId(); + + 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); + + // 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. + AMQFrame response = ChannelCloseOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0); + session.writeFrame(response); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelCloseOkHandler.java new file mode 100644 index 0000000000..ad5604e7ea --- /dev/null +++ b/Final2/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, AMQMethodEvent evt) throws AMQException + { + int channelId = evt.getChannelId(); + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.java new file mode 100644 index 0000000000..bfa170cfc5 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelFlowHandler.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.ChannelFlowBody; +import org.apache.qpid.framing.ChannelFlowOkBody; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + ChannelFlowBody body = evt.getMethod(); + + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + channel.setSuspended(!body.active); + _logger.debug("Channel.Flow for channel " + evt.getChannelId() + ", active=" + body.active); + + // 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. + AMQFrame response = ChannelFlowOkBody.createAMQFrame(evt.getChannelId(), + (byte)8, (byte)0, // AMQP version (major, minor) + body.active); // active + session.writeFrame(response); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.java new file mode 100644 index 0000000000..03fc7a3926 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ChannelOpenHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ChannelOpenOkBody; +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; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + + final AMQChannel channel = new AMQChannel(session,evt.getChannelId(), virtualHost.getMessageStore(), + virtualHost.getExchangeRegistry()); + session.addChannel(channel); + // 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. + AMQFrame response = ChannelOpenOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0); + session.writeFrame(response); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java new file mode 100644 index 0000000000..b086cad67f --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.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.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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + final ConnectionCloseBody body = evt.getMethod(); + if (_logger.isInfoEnabled()) + { + _logger.info("ConnectionClose received with reply code/reply text " + body.replyCode + "/" + + body.replyText + " for " + session); + } + try + { + session.closeSession(); + } + catch (Exception e) + { + _logger.error("Error closing protocol session: " + e, e); + } + // 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. + final AMQFrame response = ConnectionCloseOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0); + session.writeFrame(response); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java new file mode 100644 index 0000000000..853f4df435 --- /dev/null +++ b/Final2/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, AMQMethodEvent evt) 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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java new file mode 100644 index 0000000000..30a40c5a75 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionOpenBody; +import org.apache.qpid.framing.ConnectionOpenOkBody; +import org.apache.qpid.protocol.AMQConstant; +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; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.security.access.AccessResult; +import org.apache.qpid.server.security.access.AccessRights; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + ConnectionOpenBody body = evt.getMethod(); + + //ignore leading '/' + String virtualHostName; + if ((body.virtualHost != null) && body.virtualHost.charAt(0) == '/') + { + virtualHostName = new StringBuilder(body.virtualHost.subSequence(1, body.virtualHost.length())).toString(); + } + else + { + virtualHostName = body.virtualHost == null ? null : String.valueOf(body.virtualHost); + } + + VirtualHost virtualHost = stateManager.getVirtualHostRegistry().getVirtualHost(virtualHostName); + + if (virtualHost == null) + { + throw body.getConnectionException(AMQConstant.NOT_FOUND, "Unknown virtual host: '" + virtualHostName + "'"); + } + else + { + session.setVirtualHost(virtualHost); + + AccessResult result = virtualHost.getAccessManager().isAuthorized(virtualHost, session.getAuthorizedID(), AccessRights.Rights.ANY); + + switch (result.getStatus()) + { + default: + case REFUSED: + String error = "Any access denied to vHost '" + virtualHostName + "' by " + + result.getAuthorizer(); + + _logger.warn(error); + + throw body.getConnectionException(AMQConstant.ACCESS_REFUSED, error); + case GRANTED: + _logger.info("Granted any access to vHost '" + virtualHostName + "' for " + session.getAuthorizedID() + + " by '" + result.getAuthorizer() + "'"); + } + + // See Spec (0.8.2). Section 3.1.2 Virtual Hosts + if (session.getContextKey() == null) + { + session.setContextKey(generateClientID()); + } + + // 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. + AMQFrame response = ConnectionOpenOkBody.createAMQFrame((short) 0, + (byte) 8, (byte) 0, // AMQP version (major, minor) + body.virtualHost); + stateManager.changeState(AMQState.CONNECTION_OPEN); + session.writeFrame(response); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java new file mode 100644 index 0000000000..fef00942a0 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.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.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.AMQFrame; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionSecureOkBody; +import org.apache.qpid.framing.ConnectionTuneBody; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + ConnectionSecureOkBody body = evt.getMethod(); + + //fixme Vhost not defined yet + //session.getVirtualHost().getAuthenticationManager(); + AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager(); + + SaslServer ss = session.getSaslServer(); + if (ss == null) + { + throw new AMQException("No SASL context set up in session"); + } + + AuthenticationResult authResult = authMgr.authenticate(ss, body.response); + switch (authResult.status) + { + case ERROR: + // Can't do this as we violate protocol. Need to send Close + // throw new AMQException(AMQConstant.NOT_ALLOWED.getCode(), AMQConstant.NOT_ALLOWED.getName()); + _logger.info("Authentication failed"); + stateManager.changeState(AMQState.CONNECTION_CLOSING); + // 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. + AMQFrame close = ConnectionCloseBody.createAMQFrame(0, + (byte)8, (byte)0, // AMQP version (major, minor) + ConnectionCloseBody.getClazz((byte)8, (byte)0), // classId + ConnectionCloseBody.getMethod((byte)8, (byte)0), // methodId + AMQConstant.NOT_ALLOWED.getCode(), // replyCode + AMQConstant.NOT_ALLOWED.getName()); // replyText + session.writeFrame(close); + disposeSaslServer(session); + break; + case SUCCESS: + _logger.info("Connected as: " + ss.getAuthorizationID()); + stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); + // TODO: Check the value of channelMax here: This should be the max + // value of a 2-byte unsigned integer (as channel is only 2 bytes on the wire), + // not Integer.MAX_VALUE (which is signed 4 bytes). + // 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. + AMQFrame tune = ConnectionTuneBody.createAMQFrame(0, + (byte)8, (byte)0, // AMQP version (major, minor) + Integer.MAX_VALUE, // channelMax + ConnectionStartOkMethodHandler.getConfiguredFrameSize(), // frameMax + HeartbeatConfig.getInstance().getDelay()); // heartbeat + session.writeFrame(tune); + session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + disposeSaslServer(session); + break; + case CONTINUE: + stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); + // 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. + AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0, + (byte)8, (byte)0, // AMQP version (major, minor) + authResult.challenge); // challenge + session.writeFrame(challenge); + } + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java new file mode 100644 index 0000000000..bb9eeed1d4 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.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.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.AMQFrame; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionStartOkBody; +import org.apache.qpid.framing.ConnectionTuneBody; +import org.apache.qpid.framing.ConnectionCloseBody; +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 StateAwareMethodListener getInstance() + { + return _instance; + } + + private ConnectionStartOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + final ConnectionStartOkBody body = evt.getMethod(); + _logger.info("SASL Mechanism selected: " + body.mechanism); + _logger.info("Locale selected: " + body.locale); + + AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();//session.getVirtualHost().getAuthenticationManager(); + + SaslServer ss = null; + try + { + ss = authMgr.createSaslServer(String.valueOf(body.mechanism), session.getLocalFQDN()); + + if (ss == null) + { + throw body.getConnectionException(AMQConstant.RESOURCE_ERROR, "Unable to create SASL Server:" + body.mechanism + ); + } + + session.setSaslServer(ss); + + AuthenticationResult authResult = authMgr.authenticate(ss, body.response); + + //save clientProperties + if (session.getClientProperties() == null) + { + session.setClientProperties(body.clientProperties); + } + + switch (authResult.status) + { + case ERROR: + _logger.info("Authentication failed"); + stateManager.changeState(AMQState.CONNECTION_CLOSING); + // 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. + AMQFrame close = ConnectionCloseBody.createAMQFrame(0, + (byte) 8, (byte) 0, // AMQP version (major, minor) + ConnectionCloseBody.getClazz((byte) 8, (byte) 0), // classId + ConnectionCloseBody.getMethod((byte) 8, (byte) 0), // methodId + AMQConstant.NOT_ALLOWED.getCode(), // replyCode + AMQConstant.NOT_ALLOWED.getName()); // replyText + session.writeFrame(close); + disposeSaslServer(session); + break; + + case SUCCESS: + _logger.info("Connected as: " + ss.getAuthorizationID()); + session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + + stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); + // 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. + AMQFrame tune = ConnectionTuneBody.createAMQFrame(0, + (byte) 8, (byte) 0, // AMQP version (major, minor) + Integer.MAX_VALUE, // channelMax + getConfiguredFrameSize(), // frameMax + HeartbeatConfig.getInstance().getDelay()); // heartbeat + session.writeFrame(tune); + break; + case CONTINUE: + stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); + // 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. + AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0, + (byte) 8, (byte) 0, // AMQP version (major, minor) + authResult.challenge); // challenge + session.writeFrame(challenge); + } + } + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java new file mode 100644 index 0000000000..ab7695955c --- /dev/null +++ b/Final2/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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + ConnectionTuneOkBody body = evt.getMethod(); + if (_logger.isDebugEnabled()) + { + _logger.debug(body); + } + stateManager.changeState(AMQState.CONNECTION_NOT_OPENED); + session.initHeartbeats(body.heartbeat); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java new file mode 100644 index 0000000000..162c03b025 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.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.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ExchangeBoundBody; +import org.apache.qpid.framing.ExchangeBoundOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.exchange.Exchange; +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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + // 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. + byte major = (byte)8; + byte minor = (byte)0; + + ExchangeBoundBody body = evt.getMethod(); + + AMQShortString exchangeName = body.exchange; + AMQShortString queueName = body.queue; + AMQShortString routingKey = body.routingKey; + if (exchangeName == null) + { + throw new AMQException("Exchange exchange must not be null"); + } + Exchange exchange = virtualHost.getExchangeRegistry().getExchange(exchangeName); + AMQFrame response; + if (exchange == null) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + EXCHANGE_NOT_FOUND, // replyCode + new AMQShortString("Exchange " + exchangeName + " not found")); // replyText + } + else if (routingKey == null) + { + if (queueName == null) + { + if (exchange.hasBindings()) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + OK, // replyCode + null); // replyText + } + else + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + NO_BINDINGS, // replyCode + null); // replyText + } + } + else + { + + AMQQueue queue = queueRegistry.getQueue(queueName); + if (queue == null) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + QUEUE_NOT_FOUND, // replyCode + new AMQShortString("Queue " + queueName + " not found")); // replyText + } + else + { + if (exchange.isBound(queue)) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + OK, // replyCode + null); // replyText + } + else + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + 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) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + QUEUE_NOT_FOUND, // replyCode + new AMQShortString("Queue " + queueName + " not found")); // replyText + } + else + { + if (exchange.isBound(body.routingKey, queue)) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + OK, // replyCode + null); // replyText + } + else + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + SPECIFIC_QUEUE_NOT_BOUND_WITH_RK, // replyCode + new AMQShortString("Queue " + queueName + " not bound with routing key " + + body.routingKey + " to exchange " + exchangeName)); // replyText + } + } + } + else + { + if (exchange.isBound(body.routingKey)) + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + OK, // replyCode + null); // replyText + } + else + { + // AMQP version change: Be aware of possible changes to parameter order as versions change. + response = ExchangeBoundOkBody.createAMQFrame(evt.getChannelId(), + major, minor, // AMQP version (major, minor) + NO_QUEUE_BOUND_WITH_RK, // replyCode + new AMQShortString("No queue bound with routing key " + body.routingKey + + " to exchange " + exchangeName)); // replyText + } + } + session.writeFrame(response); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java new file mode 100644 index 0000000000..8ab8366d9e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeclareHandler.java @@ -0,0 +1,114 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.AMQFrame; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + + final ExchangeDeclareBody body = evt.getMethod(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Request to declare exchange of type " + body.type + " with name " + body.exchange); + } + synchronized(exchangeRegistry) + { + Exchange exchange = exchangeRegistry.getExchange(body.exchange); + + + + if (exchange == null) + { + if(body.passive && ((body.type == null) || body.type.length() ==0)) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Unknown exchange: " + body.exchange); + } + else + { + try + { + + exchange = exchangeFactory.createExchange(body.exchange == null ? null : body.exchange.intern(), + body.type == null ? null : body.type.intern(), + body.durable, + body.passive, body.ticket); + exchangeRegistry.registerExchange(exchange); + } + catch(AMQUnknownExchangeType e) + { + throw body.getConnectionException(AMQConstant.COMMAND_INVALID, "Unknown exchange: " + body.exchange,e); + } + } + } + else if (!exchange.getType().equals(body.type)) + { + + throw new AMQConnectionException(AMQConstant.NOT_ALLOWED, "Attempt to redeclare exchange: " + body.exchange + " of type " + exchange.getType() + " to " + body.type +".",body.getClazz(), body.getMethod(),body.getMajor(),body.getMinor()); + } + + } + if(!body.nowait) + { + // 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. + AMQFrame response = ExchangeDeclareOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0); + session.writeFrame(response); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.java new file mode 100644 index 0000000000..f9926c399c --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeDeleteHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ExchangeDeleteBody; +import org.apache.qpid.framing.ExchangeDeleteOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + + ExchangeDeleteBody body = evt.getMethod(); + try + { + exchangeRegistry.unregisterExchange(body.exchange, body.ifUnused); + // 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. + AMQFrame response = ExchangeDeleteOkBody.createAMQFrame(evt.getChannelId(), (byte)8, (byte)0); + session.writeFrame(response); + } + catch (ExchangeInUseException e) + { + // TODO: sort out consistent channel close mechanism that does all clean up etc. + } + + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java new file mode 100644 index 0000000000..ac516b6133 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java new file mode 100644 index 0000000000..3e68069838 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.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.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueBindOkBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + final QueueBindBody body = evt.getMethod(); + final AMQQueue queue; + if (body.queue == null) + { + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + queue = channel.getDefaultQueue(); + + if (queue == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "No default queue defined on channel and queue was null"); + } + + if (body.routingKey == null) + { + body.routingKey = queue.getName(); + } + } + else + { + queue = queueRegistry.getQueue(body.queue); + } + + if (queue == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist."); + } + final Exchange exch = exchangeRegistry.getExchange(body.exchange); + if (exch == null) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Exchange " + body.exchange + " does not exist."); + } + + if (body.routingKey != null) + { + body.routingKey = body.routingKey.intern(); + } + + try + { + if (!exch.isBound(body.routingKey, body.arguments, queue)) + { + queue.bind(body.routingKey, body.arguments, exch); + } + } + catch (AMQInvalidRoutingKeyException rke) + { + throw body.getChannelException(AMQConstant.INVALID_ROUTING_KEY, body.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 " + body.routingKey); + } + if (!body.nowait) + { + // 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. + final AMQFrame response = QueueBindOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0); + session.writeFrame(response); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java new file mode 100644 index 0000000000..29697542be --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.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.server.handler; + +import java.text.MessageFormat; +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.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.QueueDeclareOkBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +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.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +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 _log = Logger.getLogger(QueueDeclareHandler.class); + + private static final QueueDeclareHandler _instance = new QueueDeclareHandler(); + + public static QueueDeclareHandler getInstance() + { + return _instance; + } + + @Configured(path = "queue.auto_register", defaultValue = "false") + public boolean autoRegister; + + private final AtomicInteger _counter = new AtomicInteger(); + + + protected QueueDeclareHandler() + { + Configurator.configure(this); + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore store = virtualHost.getMessageStore(); + + QueueDeclareBody body = evt.getMethod(); + + // if we aren't given a queue name, we create one which we return to the client + if (body.queue == null) + { + body.queue = createName(); + } + + AMQQueue queue; + //TODO: do we need to check that the queue already exists with exactly the same "configuration"? + + synchronized (queueRegistry) + { + + + + if (((queue = queueRegistry.getQueue(body.queue)) == null)) + { + if(body.queue != null) + { + body.queue = body.queue.intern(); + } + + if (body.passive) + { + String msg = "Queue: " + body.queue + " not found on VirtualHost(" + virtualHost + ")."; + throw body.getChannelException(AMQConstant.NOT_FOUND, msg); + } + else + { + queue = createQueue(body, virtualHost, session); + if (queue.isDurable() && !queue.isAutoDelete()) + { + store.createQueue(queue); + } + queueRegistry.registerQueue(queue); + if (autoRegister) + { + Exchange defaultExchange = exchangeRegistry.getDefaultExchange(); + + queue.bind(body.queue, null, defaultExchange); + _log.info("Queue " + body.queue + " 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('" + body.queue + "')," + + " as exclusive queue with same name " + + "declared on another client ID('" + + queue.getOwner() + "')"); + } + + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + //set this as the default queue on the channel: + channel.setDefaultQueue(queue); + } + + if (!body.nowait) + { + // 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. + AMQFrame response = QueueDeclareOkBody.createAMQFrame(evt.getChannelId(), + (byte) 8, (byte) 0, // AMQP version (major, minor) + queue.getConsumerCount(), // consumerCount + queue.getMessageCount(), // messageCount + body.queue); // queue + _log.info("Queue " + body.queue + " declared successfully"); + session.writeFrame(response); + } + } + + protected AMQShortString createName() + { + return new AMQShortString("tmp_" + UUID.randomUUID()); + } + + protected AMQQueue createQueue(QueueDeclareBody body, VirtualHost virtualHost, final AMQProtocolSession session) + throws AMQException + { + final QueueRegistry registry = virtualHost.getQueueRegistry(); + AMQShortString owner = body.exclusive ? session.getContextKey() : null; + final AMQQueue queue = new AMQQueue(body.queue, body.durable, owner, body.autoDelete, virtualHost); + final AMQShortString queueName = queue.getName(); + + if (body.exclusive && !body.durable) + { + 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 + + Configuration virtualHostDefaultQueueConfiguration = VirtualHostConfiguration.getDefaultQueueConfiguration(queue); + if (virtualHostDefaultQueueConfiguration != null) + { + Configurator.configure(queue, virtualHostDefaultQueueConfiguration); + } + + return queue; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java new file mode 100644 index 0000000000..eb7089afdc --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQConstant; +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.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; + +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + MessageStore store = virtualHost.getMessageStore(); + + QueueDeleteBody body = evt.getMethod(); + AMQQueue queue; + if (body.queue == null) + { + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + //get the default queue on the channel: + queue = channel.getDefaultQueue(); + } + else + { + queue = queueRegistry.getQueue(body.queue); + } + + if (queue == null) + { + if (_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist."); + } + } + else + { + if (body.ifEmpty && !queue.isEmpty()) + { + throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.queue + " is not empty."); + } + else if (body.ifUnused && !queue.isUnused()) + { + // TODO - Error code + throw body.getChannelException(AMQConstant.IN_USE, "Queue: " + body.queue + " is still used."); + + } + else + { + int purged = queue.delete(body.ifUnused, body.ifEmpty); + + if (queue.isDurable()) + { + store.removeQueue(queue.getName()); + } + + // 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(QueueDeleteOkBody.createAMQFrame(evt.getChannelId(), + (byte) 8, (byte) 0, // AMQP version (major, minor) + purged)); // messageCount + } + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java new file mode 100644 index 0000000000..3e1937bb43 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ + +package org.apache.qpid.server.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueuePurgeBody; +import org.apache.qpid.framing.QueuePurgeOkBody; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +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.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.AMQChannel; + +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + AMQChannel channel = session.getChannel(evt.getChannelId()); + + QueuePurgeBody body = evt.getMethod(); + AMQQueue queue; + if(body.queue == null) + { + + if (channel == null) + { + throw body.getChannelNotFoundException(evt.getChannelId()); + } + + //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.queue); + } + + if(queue == null) + { + if(_failIfNotFound) + { + throw body.getChannelException(AMQConstant.NOT_FOUND, "Queue " + body.queue + " does not exist."); + } + } + else + { + long purged = queue.clearQueue(channel.getStoreContext()); + + + if(!body.nowait) + { + // 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(QueuePurgeOkBody.createAMQFrame(evt.getChannelId(), + (byte)8, (byte)0, // AMQP version (major, minor) + purged)); // messageCount + } + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java new file mode 100644 index 0000000000..3d7ec286f9 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.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.TxCommitBody; +import org.apache.qpid.framing.TxCommitOkBody; +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 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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + try + { + if (_log.isDebugEnabled()) + { + _log.debug("Commit received on channel " + evt.getChannelId()); + } + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw evt.getMethod().getChannelNotFoundException(evt.getChannelId()); + } + + channel.commit(); + // 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(TxCommitOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0)); + channel.processReturns(session); + } + catch (AMQException e) + { + throw evt.getMethod().getChannelException(e.getErrorCode(), "Failed to commit: " + e.getMessage()); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.java new file mode 100644 index 0000000000..f747f7a840 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxRollbackHandler.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.qpid.AMQException; +import org.apache.qpid.framing.TxRollbackBody; +import org.apache.qpid.framing.TxRollbackOkBody; +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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + try + { + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw evt.getMethod().getChannelNotFoundException(evt.getChannelId()); + } + + channel.rollback(); + // 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(TxRollbackOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0)); + //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 evt.getMethod().getChannelException(e.getErrorCode(), "Failed to rollback: " + e.getMessage()); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/handler/TxSelectHandler.java new file mode 100644 index 0000000000..a9e478e301 --- /dev/null +++ b/Final2/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.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, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + + AMQChannel channel = session.getChannel(evt.getChannelId()); + + if (channel == null) + { + throw evt.getMethod().getChannelNotFoundException(evt.getChannelId()); + } + + channel.setLocalTransactional(); + + // 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(TxSelectOkBody.createAMQFrame(evt.getChannelId(), (byte) 8, (byte) 0)); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/jms/JmsConsumer.java new file mode 100644 index 0000000000..c08fae4e4e --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java new file mode 100644 index 0000000000..a2c2bd62a2 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java new file mode 100644 index 0000000000..84526dbc11 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java new file mode 100644 index 0000000000..4caae2b26f --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -0,0 +1,283 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.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 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 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 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) + { + env.put("jmx.remote.profiles", "SASL/PLAIN"); + env.put("jmx.remote.sasl.callback.handler", new UserCallbackHandler(db)); + } + + // 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); + } + } + + /** This class is used for SASL enabled JMXConnector for performing user authentication. */ + private class UserCallbackHandler implements CallbackHandler + { + private final PrincipalDatabase _principalDatabase; + + protected UserCallbackHandler(PrincipalDatabase database) + { + _principalDatabase = database; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + // Retrieve callbacks + NameCallback ncb = null; + PasswordCallback pcb = null; + for (int i = 0; i < callbacks.length; i++) + { + if (callbacks[i] instanceof NameCallback) + { + ncb = (NameCallback) callbacks[i]; + } + else if (callbacks[i] instanceof PasswordCallback) + { + pcb = (PasswordCallback) callbacks[i]; + } + else if (callbacks[i] instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callbacks[i]).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + + boolean authorized = false; + // Process retrieval of password; can get password if username is available in NameCallback + if ((ncb != null) && (pcb != null)) + { + String username = ncb.getDefaultName(); + try + { + authorized = _principalDatabase.verifyPassword(username, pcb.getPassword()); + } + catch (AccountNotFoundException e) + { + IOException ioe = new IOException("User not authorized. " + e); + ioe.initCause(e); + throw ioe; + } + } + + if (!authorized) + { + throw new IOException("User not authorized."); + } + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanAttribute.java new file mode 100644 index 0000000000..7d42297699 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanConstructor.java new file mode 100644 index 0000000000..9138e03085 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanDescription.java new file mode 100644 index 0000000000..448fed3280 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanIntrospector.java new file mode 100644 index 0000000000..0c2ec2aebd --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java new file mode 100644 index 0000000000..4fb260472d --- /dev/null +++ b/Final2/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.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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperation.java new file mode 100644 index 0000000000..a2dca3e51d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/MBeanOperationParameter.java new file mode 100644 index 0000000000..aba5ec70d8 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/Managable.java new file mode 100644 index 0000000000..166a2a376d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedBroker.java new file mode 100644 index 0000000000..45e2e91ed7 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObject.java new file mode 100644 index 0000000000..42ea8921a4 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagedObjectRegistry.java new file mode 100644 index 0000000000..d8d87ef881 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/ManagementConfiguration.java new file mode 100644 index 0000000000..042f626e8b --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/management/NoopManagedObjectRegistry.java new file mode 100644 index 0000000000..b4fbed6948 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverter.java new file mode 100644 index 0000000000..e01c5aabbf --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.java new file mode 100644 index 0000000000..8366c426dd --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/output/ProtocolOutputConverterRegistry.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. + * + */ + +/* + * 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.output.amqp0_8.ProtocolOutputConverterImpl; +import org.apache.qpid.server.protocol.AMQProtocolSession; + +import java.util.Map; +import java.util.HashMap; + +public class ProtocolOutputConverterRegistry +{ + + private static final Map> _registry = + new HashMap>(); + + + static + { + register((byte) 8, (byte) 0, ProtocolOutputConverterImpl.getInstanceFactory()); + } + + private static void register(byte major, byte minor, Factory converter) + { + if(!_registry.containsKey(major)) + { + _registry.put(major, new HashMap()); + } + _registry.get(major).put(minor, converter); + } + + + public static ProtocolOutputConverter getConverter(AMQProtocolSession session) + { + return _registry.get(session.getProtocolMajorVersion()).get(session.getProtocolMinorVersion()).newInstance(session); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java new file mode 100644 index 0000000000..8462ed9557 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -0,0 +1,282 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 + { + ByteBuffer deliver = createEncodedDeliverFrame(message, channelId, deliveryTag, consumerTag); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final AMQMessageHandle messageHandle = message.getMessageHandle(); + final StoreContext storeContext = message.getStoreContext(); + final Long messageId = message.getMessageId(); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + + 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,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext,messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getRegistry().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(); + final long messageId = message.getMessageId(); + + ByteBuffer deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize); + + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + message.getContentHeaderBody()); + + final int bodyCount = messageHandle.getBodyCount(storeContext,messageId); + 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,messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent); + writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for(int i = 1; i < bodyCount; i++) + { + cb = messageHandle.getContentChunk(storeContext, messageId, i); + writeFrame(new AMQFrame(channelId, getProtocolSession().getRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + private ByteBuffer createEncodedDeliverFrame(AMQMessage message, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + AMQFrame deliverFrame = BasicDeliverBody.createAMQFrame(channelId, getProtocolMajorVersion(), + getProtocolMinorVersion(), + consumerTag, + deliveryTag, pb.getExchange(), messageHandle.isRedelivered(), + pb.getRoutingKey()); + + + return deliverFrame.toByteBuffer(); + } + + private ByteBuffer createEncodedGetOkFrame(AMQMessage message, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + final MessagePublishInfo pb = message.getMessagePublishInfo(); + final AMQMessageHandle messageHandle = message.getMessageHandle(); + + AMQFrame getOkFrame = BasicGetOkBody.createAMQFrame(channelId, + getProtocolMajorVersion(), + getProtocolMinorVersion(), + deliveryTag, pb.getExchange(), + queueSize, + messageHandle.isRedelivered(), + pb.getRoutingKey()); + + return getOkFrame.toByteBuffer(); + } + + public byte getProtocolMinorVersion() + { + return getProtocolSession().getProtocolMinorVersion(); + } + + public byte getProtocolMajorVersion() + { + return getProtocolSession().getProtocolMajorVersion(); + } + + private ByteBuffer createEncodedReturnFrame(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + AMQFrame returnFrame = BasicReturnBody.createAMQFrame(channelId, + getProtocolMajorVersion(), + getProtocolMinorVersion(), + message.getMessagePublishInfo().getExchange(), + replyCode, replyText, + message.getMessagePublishInfo().getRoutingKey()); + + return returnFrame.toByteBuffer(); + } + + public void writeReturn(AMQMessage message, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + ByteBuffer 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[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent); + writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, + new AMQDataBlock[]{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) + { + + writeFrame(BasicCancelOkBody.createAMQFrame(channelId, + getProtocolMajorVersion(), + getProtocolMinorVersion(), + consumerTag // consumerTag + )); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java new file mode 100644 index 0000000000..5bfd47b469 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java @@ -0,0 +1,773 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.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.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 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; + + private 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 VersionSpecificRegistry _registry = MainRegistry.getVersionSpecificRegistry(_protocolVersion); + private List _closingChannelsList = new ArrayList(); + private ProtocolOutputConverter _protocolOutputConverter; + private Principal _authorizedID; + + 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; + + } + + // this(session, queueRegistry, exchangeRegistry, codecFactory, new AMQStateManager()); + } + + 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); + } + + if (body instanceof AMQMethodBody) + { + methodFrameReceived(channelId, (AMQMethodBody) body); + } + else if (body instanceof ContentHeaderBody) + { + contentHeaderReceived(channelId, (ContentHeaderBody) body); + } + else if (body instanceof ContentBody) + { + contentBodyReceived(channelId, (ContentBody) body); + } + else if (body instanceof HeartbeatBody) + { + // NO OP + } + else + { + _logger.warn("Unrecognised frame " + frame.getClass().getName()); + } + } + + private void protocolInitiationReceived(ProtocolInitiation pi) + { + // this ensures the codec never checks for a PI message again + ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false); + try + { + pi.checkVersion(); // Fails if not correct + + // This sets the protocol version (and hence framing classes) for this session. + setProtocolVersion(pi._protocolMajor, pi._protocolMinor); + + String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms(); + + String locales = "en_US"; + + // Interfacing with generated code - be aware of possible changes to parameter order as versions change. + AMQFrame response = + ConnectionStartBody.createAMQFrame((short) 0, getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) + locales.getBytes(), // locales + mechanisms.getBytes(), // mechanisms + null, // serverProperties + (short) getProtocolMajorVersion(), // versionMajor + (short) getProtocolMinorVersion()); // versionMinor + _minaProtocolSession.write(response); + } + catch (AMQException e) + { + _logger.error("Received incorrect protocol initiation", e); + + _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 + + } + } + + private void methodFrameReceived(int channelId, AMQMethodBody methodBody) + { + + final AMQMethodEvent evt = new AMQMethodEvent(channelId, methodBody); + + // Check that this channel is not closing + if (channelAwaitingClosure(channelId)) + { + if ((evt.getMethod() instanceof ChannelCloseOkBody)) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure - processing close-ok"); + } + } + else + { + if (_logger.isInfoEnabled()) + { + _logger.info("Channel[" + channelId + "] awaiting closure ignoring"); + } + + return; + } + } + + 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()); + } + + closeSession(); + + AMQConnectionException ce = + evt.getMethod().getConnectionException(AMQConstant.CHANNEL_ERROR, + AMQConstant.CHANNEL_ERROR.getName().toString()); + + _stateManager.changeState(AMQState.CONNECTION_CLOSING); + writeFrame(ce.getCloseFrame(channelId)); + } + } + catch (AMQConnectionException e) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e.getMessage()); + } + + closeSession(); + _stateManager.changeState(AMQState.CONNECTION_CLOSING); + writeFrame(e.getCloseFrame(channelId)); + } + } + catch (Exception e) + { + _stateManager.error(e); + for (AMQMethodListener listener : _frameListeners) + { + listener.error(e); + } + + _minaProtocolSession.close(); + } + } + + private void contentHeaderReceived(int channelId, ContentHeaderBody body) throws AMQException + { + + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentHeader(body, this); + + } + + private void contentBodyReceived(int channelId, ContentBody body) throws AMQException + { + AMQChannel channel = getAndAssertChannel(channelId); + + channel.publishContentBody(body, this); + } + + /** + * 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; + _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.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(this); + markChannelawaitingCloseOk(channelId); + } + finally + { + removeChannel(channelId); + } + } + } + + public void closeChannelOk(int 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(this); + } + + _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; + closeAllChannels(); + if (_managedObject != null) + { + _managedObject.unregister(); + } + + for (Task task : _taskList) + { + task.doTask(this); + } + } + } + + public String toString() + { + return "AMQProtocolSession(" + _minaProtocolSession.getRemoteAddress() + ")"; + } + + 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())); + } + } + } + + private void setProtocolVersion(byte major, byte minor) + { + _protocolVersion = new ProtocolVersion(major, minor); + + _registry = MainRegistry.getVersionSpecificRegistry(_protocolVersion); + + _protocolOutputConverter = ProtocolOutputConverterRegistry.getConverter(this); + } + + public byte getProtocolMajorVersion() + { + return _protocolVersion.getMajorVersion(); + } + + public byte getProtocolMinorVersion() + { + return _protocolVersion.getMinorVersion(); + } + + public boolean isProtocolVersion(byte major, byte minor) + { + return (getProtocolMajorVersion() == major) && (getProtocolMinorVersion() == minor); + } + + public VersionSpecificRegistry getRegistry() + { + return _registry; + } + + public Object getClientIdentifier() + { + return _minaProtocolSession.getRemoteAddress(); + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public void setVirtualHost(VirtualHost virtualHost) throws AMQException + { + _virtualHost = virtualHost; + _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 String getClientVersion() + { + return (_clientVersion == null) ? null : _clientVersion.toString(); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQNoMethodHandlerException.java new file mode 100644 index 0000000000..a7599a3e0d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java new file mode 100644 index 0000000000..476e608b01 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.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.protocol; + +import org.apache.log4j.Logger; +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.util.SessionUtil; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQProtocolHeaderException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.HeartbeatBody; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; +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; + + + public AMQPFastProtocolHandler(Integer applicationRegistryInstance) + { + this(ApplicationRegistry.getInstance(applicationRegistryInstance)); + } + + public AMQPFastProtocolHandler(IApplicationRegistry applicationRegistry) + { + _applicationRegistry = applicationRegistry; + _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 ProtocolCodecFilter pcf = new ProtocolCodecFilter(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())); + } + + } + } + + /** + * 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) + { + amqProtocolSession.closeSession(); + } + } + + 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, throwable); + } + else + { + _logger.error("Exception caught in" + session + ", closing session explictly: " + throwable, throwable); + + // Be aware of possible changes to parameter order as versions change. + protocolSession.write(ConnectionCloseBody.createAMQFrame(0, + session.getProtocolMajorVersion(), + session.getProtocolMinorVersion(), // AMQP version (major, minor) + 0, // classId + 0, // methodId + 200, // replyCode + new AMQShortString(throwable.getMessage()) // replyText + )); + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPProtocolProvider.java new file mode 100644 index 0000000000..07c153bfe8 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java new file mode 100644 index 0000000000..390117acf6 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -0,0 +1,175 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +import javax.security.sasl.SaslServer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +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 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; + + /** @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(); + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java new file mode 100644 index 0000000000..66f928a70e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -0,0 +1,305 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.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 + { + // 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. + final AMQFrame response = + ConnectionCloseBody.createAMQFrame(0, _session.getProtocolMajorVersion(), _session.getProtocolMinorVersion(), // AMQP version (major, minor) + 0, // classId + 0, // methodId + AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + BROKER_MANAGEMENT_CONSOLE_HAS_CLOSED_THE_CONNECTION // replyText + ); + _session.writeFrame(response); + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.java new file mode 100644 index 0000000000..29d55ce763 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ExchangeInitialiser.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.protocol; + +import org.apache.qpid.AMQException; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; + +public class ExchangeInitialiser +{ + public void initialise(ExchangeFactory factory, ExchangeRegistry registry) throws AMQException{ + define(registry, factory, ExchangeDefaults.DEFAULT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); + define(registry, factory, ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); + define(registry, factory, ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS); + define(registry, factory, ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + define(registry, factory, ExchangeDefaults.FANOUT_EXCHANGE_NAME, ExchangeDefaults.FANOUT_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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/HeartbeatConfig.java new file mode 100644 index 0000000000..310deaaf55 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/ManagedConnection.java new file mode 100644 index 0000000000..e6e713ac6d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/protocol/UnknnownMessageTypeException.java new file mode 100644 index 0000000000..6e72aa062f --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java new file mode 100644 index 0000000000..dd9f32a306 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java @@ -0,0 +1,1000 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.server.protocol.AMQProtocolSession; +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.txn.TransactionalContext; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A deliverable message. + */ +public class AMQMessage +{ + /** Used for debugging purposes. */ + private static final Logger _log = Logger.getLogger(AMQMessage.class); + + /** Used in clustering. @todo What for? */ + private Set _tokens; + + /** Only use in clustering. @todo What for? */ + private AMQProtocolSession _publisher; + + private final Long _messageId; + + private final AtomicInteger _referenceCount = new AtomicInteger(1); + + private AMQMessageHandle _messageHandle; + + /** Holds the transactional context in which this message is being processed. */ + private TransactionalContext _txnContext; + + /** + * 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 boolean _deliveredToConsumer; + + /** Flag to indicate that this message requires 'immediate' delivery. */ + private boolean _immediate; + + // private Subscription _takenBySubcription; + // private AtomicBoolean _taken = new AtomicBoolean(false); + private TransientMessageData _transientMessageData = new TransientMessageData(); + + //todo: this should be part of a messageOnQueue object + private Set _rejectedBy = null; + + //todo: this should be part of a messageOnQueue object + private Map _takenMap = new HashMap(); + //todo: this should be part of a messageOnQueue object + private Map _takenBySubcriptionMap = new HashMap(); + + private final int hashcode = System.identityHashCode(this); + + //todo: this should be part of a messageOnQueue object + private long _expiration; + + public String debugIdentity() + { + return "(HC:" + hashcode + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")"; + } + + public void setExpiration() + { + long expiration = + ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getExpiration(); + long timestamp = + ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getTimestamp(); + + if (ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false)) + { + _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 boolean isReferenced() + { + return _referenceCount.get() > 0; + } + + /** + * 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(), _messageId) - 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(), + _messageId, ++_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.getRegistry().getProtocolVersionMethodConverter(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public StoreContext getStoreContext() + { + return _txnContext.getStoreContext(); + } + + private class BodyContentIterator implements Iterator + { + + private int _index = -1; + + public boolean hasNext() + { + try + { + return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1); + } + catch (AMQException e) + { + _log.error("Error getting body count: " + e, e); + + return false; + } + } + + public ContentChunk next() + { + try + { + return _messageHandle.getContentChunk(getStoreContext(), _messageId, ++_index); + } + catch (AMQException e) + { + throw new RuntimeException("Error getting content body: " + e, e); + } + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext) + { + _messageId = messageId; + _txnContext = txnContext; + _immediate = info.isImmediate(); + _transientMessageData.setMessagePublishInfo(info); + + } + + /** + * 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 + { + _messageId = messageId; + _messageHandle = factory.createMessageHandle(messageId, store, true); + _txnContext = txnConext; + _transientMessageData = null; + } + + /** + * Used in testing only. This allows the passing of the content header immediately on construction. + * + * @param messageId + * @param info + * @param txnContext + * @param contentHeader + */ + public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext, + ContentHeaderBody contentHeader) throws AMQException + { + this(messageId, info, txnContext); + setContentHeaderBody(contentHeader); + } + + /** + * Used in testing only. This allows the passing of the content header and some body fragments on construction. + * + * @param messageId + * @param info + * @param txnContext + * @param contentHeader + * @param destinationQueues + * @param contentBodies + * + * @throws AMQException + */ + public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext, + ContentHeaderBody contentHeader, List destinationQueues, List contentBodies, + MessageStore messageStore, StoreContext storeContext, MessageHandleFactory messageHandleFactory) throws AMQException + { + this(messageId, info, txnContext, contentHeader); + _transientMessageData.setDestinationQueues(destinationQueues); + routingComplete(messageStore, storeContext, messageHandleFactory); + for (ContentChunk cb : contentBodies) + { + addContentBodyFrame(storeContext, cb); + } + } + + protected AMQMessage(AMQMessage msg) throws AMQException + { + _messageId = msg._messageId; + _messageHandle = msg._messageHandle; + _txnContext = msg._txnContext; + _deliveredToConsumer = msg._deliveredToConsumer; + _transientMessageData = msg._transientMessageData; + } + + public Iterator getBodyFrameIterator(AMQProtocolSession protocolSession, int channel) + { + return new BodyFrameIterator(protocolSession, channel); + } + + public Iterator getContentBodyIterator() + { + return new BodyContentIterator(); + } + + public ContentHeaderBody getContentHeaderBody() throws AMQException + { + if (_transientMessageData != null) + { + return _transientMessageData.getContentHeaderBody(); + } + else + { + return _messageHandle.getContentHeaderBody(getStoreContext(), _messageId); + } + } + + public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) throws AMQException + { + _transientMessageData.setContentHeaderBody(contentHeaderBody); + } + + public void routingComplete(MessageStore store, StoreContext storeContext, 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 + + for (AMQQueue q : _transientMessageData.getDestinationQueues()) + { + _messageHandle.enqueue(storeContext, _messageId, q); + } + + if (_transientMessageData.getContentHeaderBody().bodySize == 0) + { + deliver(storeContext); + } + } + + public boolean addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk) throws AMQException + { + _transientMessageData.addBodyLength(contentChunk.getSize()); + final boolean allContentReceived = isAllContentReceived(); + _messageHandle.addContentBodyFrame(storeContext, _messageId, contentChunk, allContentReceived); + if (allContentReceived) + { + deliver(storeContext); + + return true; + } + else + { + return false; + } + } + + public boolean isAllContentReceived() throws AMQException + { + return _transientMessageData.isAllContentReceived(); + } + + public Long getMessageId() + { + return _messageId; + } + + /** + * 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; + } + + /** Threadsafe. Increment the reference count on the message. */ + public void incrementReference() + { + _referenceCount.incrementAndGet(); + // if (_log.isDebugEnabled()) + // { + // _log.debug("Ref count on message " + debugIdentity() + " incremented " + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); + // } + } + + /** + * 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) + { + try + { + // if (_log.isDebugEnabled()) + // { + // _log.debug("Decremented ref count on message " + debugIdentity() + " is zero; removing message" + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); + // } + + // 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, _messageId); + } + } + catch (AMQException e) + { + // to maintain consistency, we revert the count + incrementReference(); + throw new MessageCleanupException(_messageId, e); + } + } + else + { + if (count < 0) + { + throw new MessageCleanupException("Reference count for message id " + debugIdentity() + + " has gone below 0."); + } + } + } + + public void setPublisher(AMQProtocolSession publisher) + { + _publisher = publisher; + } + + public AMQProtocolSession getPublisher() + { + return _publisher; + } + + /** + * Called selectors to determin if the message has already been sent + * + * @return _deliveredToConsumer + */ + public boolean getDeliveredToConsumer() + { + return _deliveredToConsumer; + } + + public boolean isTaken(AMQQueue queue) + { + // return _taken.get(); + + synchronized (this) + { + AtomicBoolean taken = _takenMap.get(queue); + if (taken == null) + { + taken = new AtomicBoolean(false); + _takenMap.put(queue, taken); + } + + return taken.get(); + } + } + + public boolean taken(AMQQueue queue, Subscription sub) + { + // if (_taken.getAndSet(true)) + // { + // return true; + // } + // else + // { + // _takenBySubcription = sub; + // return false; + // } + + synchronized (this) + { + AtomicBoolean taken = _takenMap.get(queue); + if (taken == null) + { + taken = new AtomicBoolean(false); + } + + if (taken.getAndSet(true)) + { + return true; + } + else + { + _takenMap.put(queue, taken); + _takenBySubcriptionMap.put(queue, sub); + + return false; + } + } + } + + public void release(AMQQueue queue) + { + if (_log.isTraceEnabled()) + { + _log.trace("Releasing Message:" + debugIdentity()); + } + + // _taken.set(false); + // _takenBySubcription = null; + + synchronized (this) + { + AtomicBoolean taken = _takenMap.get(queue); + if (taken == null) + { + taken = new AtomicBoolean(false); + } + else + { + taken.set(false); + } + + _deliveredToConsumer = false; + _takenMap.put(queue, taken); + _takenBySubcriptionMap.put(queue, null); + } + } + + public boolean checkToken(Object token) + { + + if (_tokens == null) + { + _tokens = new HashSet(); + } + + if (_tokens.contains(token)) + { + return true; + } + else + { + _tokens.add(token); + + return false; + } + } + + /** + * Registers a queue to which this message is to be delivered. This is called from the exchange when it is routing + * the message. This will be called before any content bodies have been received so that the choice of + * AMQMessageHandle implementation can be picked based on various criteria. + * + * @param queue the queue + * + * @throws org.apache.qpid.AMQException if there is an error enqueuing the message + */ + public void enqueue(AMQQueue queue) throws AMQException + { + _transientMessageData.addDestinationQueue(queue); + } + + /** + * NOTE: Think about why you are using this method. Normal usages would want to do + * AMQQueue.dequeue(StoreContext, AMQMessage) + * This will keep the queue statistics up-to-date. + * Currently this method is only called _correctly_ from AMQQueue dequeue. + * Ideally we would have a better way for the queue to dequeue the message. + * Especially since enqueue isn't the recipriocal of this method. + * @deprecated + * @param storeContext + * @param queue + * @throws AMQException + */ + void dequeue(StoreContext storeContext, AMQQueue queue) throws AMQException + { + _messageHandle.dequeue(storeContext, _messageId, queue); + } + + public boolean isPersistent() throws AMQException + { + if (_transientMessageData != null) + { + return _transientMessageData.isPersistent(); + } + else + { + return _messageHandle.isPersistent(getStoreContext(), _messageId); + } + } + + /** + * Called to enforce the 'immediate' flag. + * + * @throws NoConsumersException if the message is marked for immediate delivery but has not been marked as delivered + * to a consumer + */ + public void checkDeliveredToConsumer() throws NoConsumersException + { + + if (_immediate && !_deliveredToConsumer) + { + throw new NoConsumersException(this); + } + } + + public MessagePublishInfo getMessagePublishInfo() throws AMQException + { + MessagePublishInfo pb; + if (_transientMessageData != null) + { + pb = _transientMessageData.getMessagePublishInfo(); + } + else + { + pb = _messageHandle.getMessagePublishInfo(getStoreContext(), _messageId); + } + + return pb; + } + + 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 + { + // note: If the storecontext isn't need then we can remove the getChannel() from Subscription. + + 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() + { + _deliveredToConsumer = true; + } + + private void deliver(StoreContext storeContext) throws AMQException + { + // we get a reference to the destination queues now so that we can clear the + // transient message data as quickly as possible + List destinationQueues = _transientMessageData.getDestinationQueues(); + if (_log.isDebugEnabled()) + { + _log.debug("Delivering message " + debugIdentity() + " to " + destinationQueues); + } + + 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(storeContext, _messageId, + _transientMessageData.getMessagePublishInfo(), _transientMessageData.getContentHeaderBody()); + + // 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()); + + _transientMessageData = null; + + for (AMQQueue q : destinationQueues) + { + // Increment the references to this message for each queue delivery. + incrementReference(); + // normal deliver so add this message at the end. + _txnContext.deliver(this, q, false); + } + } + finally + { + destinationQueues.clear(); + // Remove refence for routing process . Reference count should now == delivered queue count + decrementReference(storeContext); + } + } + + /* + public void writeDeliver(AMQProtocolSession protocolSession, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + ByteBuffer deliver = createEncodedDeliverFrame(protocolSession, channelId, deliveryTag, consumerTag); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + getContentHeaderBody()); + + final int bodyCount = _messageHandle.getBodyCount(getStoreContext(), _messageId); + if (bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + + protocolSession.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(getStoreContext(), _messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent); + protocolSession.writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for (int i = 1; i < bodyCount; i++) + { + cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, i); + protocolSession.writeFrame(new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + public void writeGetOk(AMQProtocolSession protocolSession, int channelId, long deliveryTag, int queueSize) throws AMQException + { + ByteBuffer deliver = createEncodedGetOkFrame(protocolSession, channelId, deliveryTag, queueSize); + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + getContentHeaderBody()); + + final int bodyCount = _messageHandle.getBodyCount(getStoreContext(), _messageId); + if (bodyCount == 0) + { + SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver, + contentHeader); + protocolSession.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(getStoreContext(), _messageId, 0); + + AMQDataBlock firstContentBody = new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb)); + AMQDataBlock[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(deliver, headerAndFirstContent); + protocolSession.writeFrame(compositeBlock); + + // + // Now start writing out the other content bodies + // + for (int i = 1; i < bodyCount; i++) + { + cb = _messageHandle.getContentChunk(getStoreContext(), _messageId, i); + protocolSession.writeFrame(new AMQFrame(channelId, protocolSession.getRegistry().getProtocolVersionMethodConverter().convertToBody(cb))); + } + + + } + + + } + + + private ByteBuffer createEncodedDeliverFrame(AMQProtocolSession protocolSession, int channelId, long deliveryTag, AMQShortString consumerTag) + throws AMQException + { + MessagePublishInfo pb = getMessagePublishInfo(); + AMQFrame deliverFrame = BasicDeliverBody.createAMQFrame(channelId, protocolSession.getProtocolMajorVersion(), (byte) 0, consumerTag, + deliveryTag, pb.getExchange(), _messageHandle.isRedelivered(), + pb.getRoutingKey()); + ByteBuffer buf = ByteBuffer.allocate((int) deliverFrame.getSize()); // XXX: Could cast be a problem? + deliverFrame.writePayload(buf); + buf.flip(); + return buf; + } + + private ByteBuffer createEncodedGetOkFrame(AMQProtocolSession protocolSession, int channelId, long deliveryTag, int queueSize) + throws AMQException + { + MessagePublishInfo pb = getMessagePublishInfo(); + AMQFrame getOkFrame = BasicGetOkBody.createAMQFrame(channelId, + protocolSession.getProtocolMajorVersion(), + protocolSession.getProtocolMinorVersion(), + deliveryTag, pb.getExchange(), + queueSize, + _messageHandle.isRedelivered(), + pb.getRoutingKey()); + ByteBuffer buf = ByteBuffer.allocate((int) getOkFrame.getSize()); // XXX: Could cast be a problem? + getOkFrame.writePayload(buf); + buf.flip(); + return buf; + } + + private ByteBuffer createEncodedReturnFrame(AMQProtocolSession protocolSession, int channelId, int replyCode, AMQShortString replyText) throws AMQException + { + AMQFrame returnFrame = BasicReturnBody.createAMQFrame(channelId, + protocolSession.getProtocolMajorVersion(), + protocolSession.getProtocolMinorVersion(), + getMessagePublishInfo().getExchange(), + replyCode, replyText, + getMessagePublishInfo().getRoutingKey()); + ByteBuffer buf = ByteBuffer.allocate((int) returnFrame.getSize()); // XXX: Could cast be a problem? + returnFrame.writePayload(buf); + buf.flip(); + return buf; + } + + public void writeReturn(AMQProtocolSession protocolSession, int channelId, int replyCode, AMQShortString replyText) + throws AMQException + { + ByteBuffer returnFrame = createEncodedReturnFrame(protocolSession, channelId, replyCode, replyText); + + AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, + getContentHeaderBody()); + + Iterator bodyFrameIterator = getBodyFrameIterator(protocolSession, 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[] headerAndFirstContent = new AMQDataBlock[]{contentHeader, firstContentBody}; + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, headerAndFirstContent); + protocolSession.writeFrame(compositeBlock); + } + else + { + CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(returnFrame, + new AMQDataBlock[]{contentHeader}); + protocolSession.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()) + { + protocolSession.writeFrame(bodyFrameIterator.next()); + } + } + */ + + public AMQMessageHandle getMessageHandle() + { + return _messageHandle; + } + + public long getSize() + { + try + { + long size = getContentHeaderBody().bodySize; + + return size; + } + catch (AMQException e) + { + _log.error(e.toString(), e); + + return 0; + } + + } + + public void restoreTransientMessageData() throws AMQException + { + TransientMessageData transientMessageData = new TransientMessageData(); + transientMessageData.setMessagePublishInfo(getMessagePublishInfo()); + transientMessageData.setContentHeaderBody(getContentHeaderBody()); + transientMessageData.addBodyLength(getContentHeaderBody().getSize()); + _transientMessageData = transientMessageData; + } + + public void clearTransientMessageData() + { + _transientMessageData = null; + } + + public String toString() + { + // return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken : " + + // _taken + " by :" + _takenBySubcription; + + return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken for queues: " + + _takenMap.toString() + " by Subs:" + _takenBySubcriptionMap.toString(); + } + + public Subscription getDeliveredSubscription(AMQQueue queue) + { + // return _takenBySubcription; + synchronized (this) + { + return _takenBySubcriptionMap.get(queue); + } + } + + 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) + { + boolean rejected = _rejectedBy != null; + + if (rejected) // We have subscriptions that rejected this message + { + return _rejectedBy.contains(subscription); + } + else // This messasge hasn't been rejected yet. + { + return rejected; + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java new file mode 100644 index 0000000000..ede55b3bbf --- /dev/null +++ b/Final2/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. + * + * The method all take a messageId to avoid having to store it in the instance - the AMQMessage container + * must already keen the messageId so it is pointless storing it twice. + */ +public interface AMQMessageHandle +{ + ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException; + + /** + * @return the number of body frames associated with this message + */ + int getBodyCount(StoreContext context, Long messageId) throws AMQException; + + /** + * @return the size of the body + */ + long getBodySize(StoreContext context, Long messageId) 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, Long messageId, int index) throws IllegalArgumentException, AMQException; + + void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody) throws AMQException; + + MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException; + + boolean isRedelivered(); + + void setRedelivered(boolean redelivered); + + boolean isPersistent(StoreContext context, Long messageId) throws AMQException; + + void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo, + ContentHeaderBody contentHeaderBody) + throws AMQException; + + void removeMessage(StoreContext storeContext, Long messageId) throws AMQException; + + void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException; + + void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException; + + long getArrivalTime(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java new file mode 100644 index 0000000000..0c52a358f7 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -0,0 +1,940 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.configuration.Configured; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.management.Managable; +import org.apache.qpid.server.management.ManagedObject; +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.virtualhost.VirtualHost; + +import javax.management.JMException; +import java.text.MessageFormat; +import java.util.List; +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; + +/** + * This is an AMQ Queue, and should not be confused with a JMS queue or any other abstraction like that. It is described + * fully in RFC 006. + */ +public class AMQQueue implements Managable, Comparable +{ + /** + * 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. + */ + public 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. + */ + public static final class ExistingSubscriptionPreventsExclusive extends AMQException + { + public ExistingSubscriptionPreventsExclusive() + { + super(""); + } + } + + private static final Logger _logger = Logger.getLogger(AMQQueue.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; + + /** Holds subscribers to the queue. */ + private final SubscriptionSet _subscribers; + + private final SubscriptionFactory _subscriptionFactory; + + private final AtomicInteger _subscriberCount = new AtomicInteger(); + + private final AtomicBoolean _isExclusive = new AtomicBoolean(); + + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + private List _deleteTaskList = new CopyOnWriteArrayList(); + + /** Manages message delivery. */ + private final DeliveryManager _deliveryMgr; + + /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */ + private final ExchangeBindings _bindings = new ExchangeBindings(this); + + /** Executor on which asynchronous delivery will be carriedout where required */ + private final Executor _asyncDelivery; + + private final AMQQueueMBean _managedObject; + + private final VirtualHost _virtualHost; + + /** 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; + + /** total messages received by the queue since startup. */ + public AtomicLong _totalMessagesReceived = new AtomicLong(); + + public int compareTo(Object o) + { + return _name.compareTo(((AMQQueue) o).getName()); + } + + public AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + throws AMQException + { + this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), + new SubscriptionSet(), new SubscriptionImpl.Factory()); + } + + protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, + VirtualHost virtualHost, SubscriptionSet subscribers) throws AMQException + { + this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers, + new SubscriptionImpl.Factory()); + } + + protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, + VirtualHost virtualHost, Executor asyncDelivery, SubscriptionSet subscribers, + SubscriptionFactory subscriptionFactory) 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; + _asyncDelivery = asyncDelivery; + + _managedObject = createMBean(); + _managedObject.register(); + + _subscribers = subscribers; + _subscriptionFactory = subscriptionFactory; + _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscribers, this); + } + + private AMQQueueMBean createMBean() throws AMQException + { + try + { + return new AMQQueueMBean(this); + } + catch (JMException ex) + { + throw new AMQException("AMQQueue MBean creation has failed ", ex); + } + } + + public AMQShortString getName() + { + return _name; + } + + public boolean isShared() + { + return _owner == null; + } + + public boolean isDurable() + { + return _durable; + } + + public AMQShortString getOwner() + { + return _owner; + } + + public boolean isAutoDelete() + { + return _autoDelete; + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + /** @return no of messages(undelivered) on the queue. */ + public int getMessageCount() + { + return _deliveryMgr.getQueueMessageCount(); + } + + /** @return List of messages(undelivered) on the queue. */ + public List getMessagesOnTheQueue() + { + return _deliveryMgr.getMessages(); + } + + /** + * Returns messages within the given range of message Ids. + * + * @param fromMessageId + * @param toMessageId + * + * @return List of messages + */ + public List getMessagesOnTheQueue(long fromMessageId, long toMessageId) + { + return _deliveryMgr.getMessages(fromMessageId, toMessageId); + } + + public long getQueueDepth() + { + return _deliveryMgr.getTotalMessageSize(); + } + + /** + * @param messageId + * + * @return AMQMessage with give id if exists. null if AMQMessage with given id doesn't exist. + */ + public AMQMessage getMessageOnTheQueue(long messageId) + { + List list = getMessagesOnTheQueue(messageId, messageId); + if ((list == null) || (list.size() == 0)) + { + return null; + } + + return list.get(0); + } + + /** + * Moves messages from this queue to another queue, and also commits the move on the message store. Delivery activity + * on the queues being moved between is suspended during the move. + * + * @param fromMessageId The first message id to move. + * @param toMessageId The last message id to move. + * @param queueName The queue to move the messages to. + * @param storeContext The context of the message store under which to perform the move. This is associated with + * the stores transactional context. + */ + public synchronized void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, + StoreContext storeContext) + { + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + + MessageStore fromStore = getVirtualHost().getMessageStore(); + MessageStore toStore = toQueue.getVirtualHost().getMessageStore(); + + if (toStore != fromStore) + { + throw new RuntimeException("Can only move messages between queues on the same message store."); + } + + try + { + // Obtain locks to prevent activity on the queues being moved between. + startMovingMessages(); + toQueue.startMovingMessages(); + + // Get the list of messages to move. + List foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); + + try + { + fromStore.beginTran(storeContext); + + // Move the messages in on the message store. + for (AMQMessage message : foundMessagesList) + { + fromStore.dequeueMessage(storeContext, _name, message.getMessageId()); + toStore.enqueueMessage(storeContext, toQueue._name, message.getMessageId()); + } + + // Commit and flush the move transcations. + try + { + fromStore.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + + // Move the messages on the in-memory queues. + toQueue.enqueueMovedMessages(storeContext, foundMessagesList); + _deliveryMgr.removeMovedMessages(foundMessagesList); + } + // Abort the move transactions on move failures. + catch (AMQException e) + { + try + { + fromStore.abortTran(storeContext); + } + catch (AMQException ae) + { + throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); + } + } + } + // Release locks to allow activity on the queues being moved between to continue. + finally + { + toQueue.stopMovingMessages(); + stopMovingMessages(); + } + } + + /** + * Copies messages on this queue to another queue, and also commits the move on the message store. Delivery activity + * on the queues being moved between is suspended during the move. + * + * @param fromMessageId The first message id to move. + * @param toMessageId The last message id to move. + * @param queueName The queue to move the messages to. + * @param storeContext The context of the message store under which to perform the move. This is associated with + * the stores transactional context. + */ + public synchronized void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, + StoreContext storeContext) + { + AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + + MessageStore fromStore = getVirtualHost().getMessageStore(); + MessageStore toStore = toQueue.getVirtualHost().getMessageStore(); + + if (toStore != fromStore) + { + throw new RuntimeException("Can only move messages between queues on the same message store."); + } + + try + { + // Obtain locks to prevent activity on the queues being moved between. + startMovingMessages(); + toQueue.startMovingMessages(); + + // Get the list of messages to move. + List foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); + + try + { + fromStore.beginTran(storeContext); + + // Move the messages in on the message store. + for (AMQMessage message : foundMessagesList) + { + toStore.enqueueMessage(storeContext, toQueue._name, message.getMessageId()); + message.takeReference(); + } + + // Commit and flush the move transcations. + try + { + fromStore.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + + // Move the messages on the in-memory queues. + toQueue.enqueueMovedMessages(storeContext, foundMessagesList); + } + // Abort the move transactions on move failures. + catch (AMQException e) + { + try + { + fromStore.abortTran(storeContext); + } + catch (AMQException ae) + { + throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); + } + } + } + // Release locks to allow activity on the queues being moved between to continue. + finally + { + toQueue.stopMovingMessages(); + stopMovingMessages(); + } + } + + /** + * Removes messages from this queue, and also commits the remove on the message store. Delivery activity + * on the queues being moved between is suspended during the remove. + * + * @param fromMessageId The first message id to move. + * @param toMessageId The last message id to move. + * @param storeContext The context of the message store under which to perform the move. This is associated with + * the stores transactional context. + */ + public synchronized void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext) + { + MessageStore fromStore = getVirtualHost().getMessageStore(); + + try + { + // Obtain locks to prevent activity on the queues being moved between. + startMovingMessages(); + + // Get the list of messages to move. + List foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId); + + try + { + fromStore.beginTran(storeContext); + + // remove the messages in on the message store. + for (AMQMessage message : foundMessagesList) + { + fromStore.dequeueMessage(storeContext, _name, message.getMessageId()); + } + + // Commit and flush the move transcations. + try + { + fromStore.commitTran(storeContext); + } + catch (AMQException e) + { + throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e); + } + + // remove the messages on the in-memory queues. + _deliveryMgr.removeMovedMessages(foundMessagesList); + } + // Abort the move transactions on move failures. + catch (AMQException e) + { + try + { + fromStore.abortTran(storeContext); + } + catch (AMQException ae) + { + throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae); + } + } + } + // Release locks to allow activity on the queues being moved between to continue. + finally + { + stopMovingMessages(); + } + } + + public void startMovingMessages() + { + _deliveryMgr.startMovingMessages(); + } + + private void enqueueMovedMessages(StoreContext storeContext, List messageList) + { + _deliveryMgr.enqueueMovedMessages(storeContext, messageList); + _totalMessagesReceived.addAndGet(messageList.size()); + } + + public void stopMovingMessages() + { + _deliveryMgr.stopMovingMessages(); + _deliveryMgr.processAsync(_asyncDelivery); + } + + /** @return MBean object associated with this Queue */ + public ManagedObject getManagedObject() + { + return _managedObject; + } + + public long getMaximumMessageSize() + { + return _maximumMessageSize; + } + + public void setMaximumMessageSize(long value) + { + _maximumMessageSize = value; + } + + public int getConsumerCount() + { + return _subscribers.size(); + } + + public int getActiveConsumerCount() + { + return _subscribers.getWeight(); + } + + public long getReceivedMessageCount() + { + return _totalMessagesReceived.get(); + } + + public long getMaximumMessageCount() + { + return _maximumMessageCount; + } + + public void setMaximumMessageCount(long value) + { + _maximumMessageCount = value; + } + + public long getMaximumQueueDepth() + { + return _maximumQueueDepth; + } + + // Sets the queue depth, the max queue size + public void setMaximumQueueDepth(long value) + { + _maximumQueueDepth = value; + } + + public long getOldestMessageArrivalTime() + { + return _deliveryMgr.getOldestMessageArrival(); + + } + + /** Removes the AMQMessage from the top of the queue. */ + public synchronized void deleteMessageFromTop(StoreContext storeContext) throws AMQException + { + _deliveryMgr.removeAMessageFromTop(storeContext, this); + } + + /** removes all the messages from the queue. */ + public synchronized long clearQueue(StoreContext storeContext) throws AMQException + { + return _deliveryMgr.clearAllMessages(storeContext); + } + + public void bind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) 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(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException + { + exchange.deregisterQueue(routingKey, this, arguments); + if (isDurable() && exchange.isDurable()) + { + _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments); + } + + _bindings.remove(routingKey, arguments, exchange); + } + + public void registerProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag, boolean acks, + FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException + { + if (incrementSubscriberCount() > 1) + { + if (isExclusive()) + { + decrementSubscriberCount(); + throw new ExistingExclusiveSubscription(); + } + else if (exclusive) + { + decrementSubscriberCount(); + throw new ExistingSubscriptionPreventsExclusive(); + } + + } + else if (exclusive) + { + setExclusive(true); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug(MessageFormat.format("Registering protocol session {0} with channel {1} and " + + "consumer tag {2} with {3}", ps, channel, consumerTag, this)); + } + + Subscription subscription = + _subscriptionFactory.createSubscription(channel, ps, consumerTag, acks, filters, noLocal, this); + + if (subscription.filtersMessages()) + { + if (_deliveryMgr.hasQueuedMessages()) + { + _deliveryMgr.populatePreDeliveryQueue(subscription); + } + } + + _subscribers.addSubscriber(subscription); + } + + private boolean isExclusive() + { + return _isExclusive.get(); + } + + private void setExclusive(boolean exclusive) + { + _isExclusive.set(exclusive); + } + + private int incrementSubscriberCount() + { + return _subscriberCount.incrementAndGet(); + } + + private int decrementSubscriberCount() + { + return _subscriberCount.decrementAndGet(); + } + + public void unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException + { + if (_logger.isDebugEnabled()) + { + _logger.debug(MessageFormat.format( + "Unregistering protocol session {0} with channel {1} and consumer tag {2} from {3}", + ps, channel, consumerTag, this)); + } + + Subscription removedSubscription; + if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel, ps, + consumerTag))) + == null) + { + throw new AMQException("Protocol session with channel " + channel + " and consumer tag " + consumerTag + + " and protocol session key " + ps.getKey() + " not registered with queue " + this); + } + + removedSubscription.close(); + setExclusive(false); + decrementSubscriberCount(); + + // if we are eligible for auto deletion, unregister from the queue registry + if (_autoDelete && _subscribers.isEmpty()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Auto-deleteing queue:" + this); + } + + autodelete(); + // 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 + removedSubscription.queueDeleted(this); + } + } + + public boolean isUnused() + { + return _subscribers.isEmpty(); + } + + public boolean isEmpty() + { + return !_deliveryMgr.hasQueuedMessages(); + } + + public int delete(boolean checkUnused, boolean checkEmpty) throws AMQException + { + if (checkUnused && !_subscribers.isEmpty()) + { + _logger.info("Will not delete " + this + " as it is in use."); + + return 0; + } + else if (checkEmpty && _deliveryMgr.hasQueuedMessages()) + { + _logger.info("Will not delete " + this + " as it is not empty."); + + return 0; + } + else + { + delete(); + + return _deliveryMgr.getQueueMessageCount(); + } + } + + public void delete() throws AMQException + { + if (!_deleted.getAndSet(true)) + { + _subscribers.queueDeleted(this); + _bindings.deregister(); + _virtualHost.getQueueRegistry().unregisterQueue(_name); + _managedObject.unregister(); + for (Task task : _deleteTaskList) + { + task.doTask(this); + } + + _deleteTaskList.clear(); + } + } + + protected void autodelete() throws AMQException + { + if (_logger.isDebugEnabled()) + { + _logger.debug(MessageFormat.format("autodeleting {0}", this)); + } + + delete(); + } + + /*public void processGet(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException + { + // fixme not sure what this is doing. should we be passing deliverFirst through here? + // This code is not used so when it is perhaps it should + _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst); + try + { + msg.checkDeliveredToConsumer(); + updateReceivedMessageCount(msg); + } + catch (NoConsumersException e) + { + // as this message will be returned, it should be removed + // from the queue: + dequeue(storeContext, msg); + } + }*/ + + // public DeliveryManager getDeliveryManager() + // { + // return _deliveryMgr; + // } + + public void process(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException + { + _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst); + try + { + msg.checkDeliveredToConsumer(); + updateReceivedMessageCount(msg); + } + catch (NoConsumersException e) + { + // as this message will be returned, it should be removed + // from the queue: + dequeue(storeContext, msg); + } + } + + public void dequeue(StoreContext storeContext, AMQMessage msg) throws FailedDequeueException + { + try + { + msg.dequeue(storeContext, this); + } + 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); + } + } + + public void deliverAsync() + { + _deliveryMgr.processAsync(_asyncDelivery); + } + + protected SubscriptionManager getSubscribers() + { + return _subscribers; + } + + protected void updateReceivedMessageCount(AMQMessage msg) throws AMQException + { + if (!msg.isRedelivered()) + { + _totalMessagesReceived.incrementAndGet(); + } + + try + { + _managedObject.checkForNotification(msg); + } + catch (JMException e) + { + throw new AMQException("Unable to get notification from manage queue: " + e, e); + } + } + + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if ((o == null) || (getClass() != o.getClass())) + { + return false; + } + + final AMQQueue amqQueue = (AMQQueue) o; + + return (_name.equals(amqQueue._name)); + } + + public int hashCode() + { + return _name.hashCode(); + } + + public String toString() + { + return "Queue(" + _name + ")@" + System.identityHashCode(this); + } + + public boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException + { + return _deliveryMgr.performGet(session, channel, acks); + } + + public QueueRegistry getQueueRegistry() + { + return _virtualHost.getQueueRegistry(); + } + + public VirtualHost getVirtualHost() + { + return _virtualHost; + } + + public static interface Task + { + public void doTask(AMQQueue queue) throws AMQException; + } + + public void addQueueDeleteTask(Task task) + { + _deleteTaskList.add(task); + } + + public long getMinimumAlertRepeatGap() + { + return _minimumAlertRepeatGap; + } + + public void setMinimumAlertRepeatGap(long minimumAlertRepeatGap) + { + _minimumAlertRepeatGap = minimumAlertRepeatGap; + } + + public long getMaximumMessageAge() + { + return _maximumMessageAge; + } + + public void setMaximumMessageAge(long maximumMessageAge) + { + _maximumMessageAge = maximumMessageAge; + } + + public void subscriberHasPendingResend(boolean hasContent, SubscriptionImpl subscription, AMQMessage msg) + { + _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, msg); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java new file mode 100644 index 0000000000..07872d7644 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -0,0 +1,471 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * 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 long currentTime = System.currentTimeMillis(); + final long thresholdTime = currentTime - _queue.getMinimumAlertRepeatGap(); + + for (NotificationCheck check : NotificationCheck.values()) + { + 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 org.apache.qpid.server.queue.AMQQueue#deleteMessageFromTop + */ + public void deleteMessageFromTop() throws JMException + { + try + { + _queue.deleteMessageFromTop(_storeContext); + } + catch (AMQException ex) + { + throw new MBeanException(ex, ex.toString()); + } + } + + /** + * @see org.apache.qpid.server.queue.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 + { + AMQMessage msg = _queue.getMessageOnTheQueue(msgId); + if (msg == null) + { + throw new OperationsException("AMQMessage with message id = " + msgId + " is not in the " + _queueName); + } + // 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); + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/AsyncDeliveryConfig.java new file mode 100644 index 0000000000..290fedcf7b --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java new file mode 100644 index 0000000000..eabc8ebf38 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java @@ -0,0 +1,1049 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.configuration.Configured; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.configuration.Configurator; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize; +import org.apache.qpid.util.MessageQueue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Queue; +import java.util.Set; +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.locks.ReentrantLock; + + +/** Manages delivery of messages on behalf of a queue */ +public class ConcurrentSelectorDeliveryManager implements DeliveryManager +{ + private static final Logger _log = Logger.getLogger(ConcurrentSelectorDeliveryManager.class); + + @Configured(path = "advanced.compressBufferOnQueue", + defaultValue = "false") + public boolean compressBufferOnQueue; + /** Holds any queued messages */ + private final MessageQueue _messages = new ConcurrentLinkedMessageQueueAtomicSize(); + + /** Ensures that only one asynchronous task is running for this manager at any time. */ + private final AtomicBoolean _processing = new AtomicBoolean(); + /** The subscriptions on the queue to whom messages are delivered */ + private final SubscriptionManager _subscriptions; + + /** + * A reference to the queue we are delivering messages for. We need this to be able to pass the code that handles + * acknowledgements a handle on the queue. + */ + private final AMQQueue _queue; + + /** + * Flag used while moving messages from this queue to another. For moving messages the async delivery should also + * stop. This flat should be set to true to stop async delivery and set to false to enable async delivery again. + */ + private AtomicBoolean _movingMessages = new AtomicBoolean(); + + /** + * Lock used to ensure that an channel that becomes unsuspended during the start of the queueing process is forced + * to wait till the first message is added to the queue. This will ensure that the _queue has messages to be + * delivered via the async thread.

Lock is used to control access to hasQueuedMessages() and over the addition + * of messages to the queue. + */ + private ReentrantLock _lock = new ReentrantLock(); + private AtomicLong _totalMessageSize = new AtomicLong(); + private AtomicInteger _extraMessages = new AtomicInteger(); + private Set _hasContent = Collections.synchronizedSet(new HashSet()); + private final Object _queueHeadLock = new Object(); + private String _processingThreadName = ""; + + + /** Used by any reaping thread to purge messages */ + private StoreContext _reapingStoreContext = new StoreContext(); + + ConcurrentSelectorDeliveryManager(SubscriptionManager subscriptions, AMQQueue queue) + { + + //Set values from configuration + Configurator.configure(this); + + if (compressBufferOnQueue) + { + _log.warn("Compressing Buffers on queue."); + } + + _subscriptions = subscriptions; + _queue = queue; + } + + + private boolean addMessageToQueue(AMQMessage msg, boolean deliverFirst) + { + // Shrink the ContentBodies to their actual size to save memory. + if (compressBufferOnQueue) + { + Iterator it = msg.getContentBodyIterator(); + while (it.hasNext()) + { + ContentChunk cb = it.next(); + cb.reduceToFit(); + } + } + + if (deliverFirst) + { + synchronized (_queueHeadLock) + { + _messages.pushHead(msg); + } + } + else + { + _messages.offer(msg); + } + + _totalMessageSize.addAndGet(msg.getSize()); + + return true; + } + + + public boolean hasQueuedMessages() + { + _lock.lock(); + try + { + return !(_messages.isEmpty() && _hasContent.isEmpty()); + } + finally + { + _lock.unlock(); + } + } + + public int getQueueMessageCount() + { + return getMessageCount(); + } + + /** + * This is an EXPENSIVE opperation to perform with a ConcurrentLinkedQueue as it must run the queue to determine + * size. The ConcurrentLinkedQueueAtomicSize uses an AtomicInteger to record the number of elements on the queue. + * + * @return int the number of messages in the delivery queue. + */ + private int getMessageCount() + { + return _messages.size() + _extraMessages.get(); + } + + + public long getTotalMessageSize() + { + return _totalMessageSize.get(); + } + + public long getOldestMessageArrival() + { + AMQMessage msg = _messages.peek(); + return msg == null ? Long.MAX_VALUE : msg.getArrivalTime(); + } + + public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, AMQMessage msg) + { + _lock.lock(); + try + { + if (hasContent) + { + _log.debug("Queue has adding subscriber content"); + _hasContent.add(subscription); + _totalMessageSize.addAndGet(msg.getSize()); + _extraMessages.addAndGet(1); + } + else + { + _log.debug("Queue has removing subscriber content"); + if (msg == null) + { + _hasContent.remove(subscription); + } + else + { + _totalMessageSize.addAndGet(-msg.getSize()); + _extraMessages.addAndGet(-1); + } + } + } + finally + { + _lock.unlock(); + } + } + + /** @return the state of the async processor. */ + public boolean isProcessingAsync() + { + return _processing.get(); + } + + /** + * Returns all the messages in the Queue + * + * @return List of messages + */ + public List getMessages() + { + _lock.lock(); + List list = new ArrayList(); + + for (AMQMessage message : _messages) + { + list.add(message); + } + _lock.unlock(); + + return list; + } + + /** + * Returns messages within the range of given messageIds + * + * @param fromMessageId + * @param toMessageId + * + * @return + */ + public List getMessages(long fromMessageId, long toMessageId) + { + if (fromMessageId <= 0 || toMessageId <= 0) + { + return null; + } + + long maxMessageCount = toMessageId - fromMessageId + 1; + + _lock.lock(); + + List foundMessagesList = new ArrayList(); + + for (AMQMessage message : _messages) + { + long msgId = message.getMessageId(); + if (msgId >= fromMessageId && msgId <= toMessageId) + { + foundMessagesList.add(message); + } + // break if the no of messages are found + if (foundMessagesList.size() == maxMessageCount) + { + break; + } + } + _lock.unlock(); + + return foundMessagesList; + } + + public void populatePreDeliveryQueue(Subscription subscription) + { + if (_log.isTraceEnabled()) + { + _log.trace("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")"); + } + + Iterator currentQueue = _messages.iterator(); + + while (currentQueue.hasNext()) + { + AMQMessage message = currentQueue.next(); + if (!message.getDeliveredToConsumer()) + { + if (subscription.hasInterest(message)) + { + subscription.enqueueForPreDelivery(message, false); + } + } + } + } + + public boolean performGet(AMQProtocolSession protocolSession, AMQChannel channel, boolean acks) throws AMQException + { + AMQMessage msg = getNextMessage(); + if (msg == null) + { + return false; + } + else + { + + 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. + if (!acks) + { + if (_log.isDebugEnabled()) + { + _log.debug("No ack mode so dequeuing message immediately: " + msg.getMessageId()); + } + _queue.dequeue(channel.getStoreContext(), msg); + } + synchronized (channel) + { + long deliveryTag = channel.getNextDeliveryTag(); + + if (acks) + { + channel.addUnacknowledgedMessage(msg, deliveryTag, null, _queue); + } + + protocolSession.getProtocolOutputConverter().writeGetOk(msg, channel.getChannelId(), + deliveryTag, _queue.getMessageCount()); + _totalMessageSize.addAndGet(-msg.getSize()); + } + + if (!acks) + { + msg.decrementReference(channel.getStoreContext()); + } + } + finally + { + msg.setDeliveredToConsumer(); + } + return true; + + } + } + + /** + * For feature of moving messages, this method is used. It sets the lock and sets the movingMessages flag, so that + * the asyn delivery is also stopped. + */ + public void startMovingMessages() + { + _movingMessages.set(true); + } + + /** + * Once moving messages to another queue is done or aborted, remove lock and unset the movingMessages flag, so that + * the async delivery can start again. + */ + public void stopMovingMessages() + { + _movingMessages.set(false); + if (_lock.isHeldByCurrentThread()) + { + _lock.unlock(); + } + } + + /** + * Messages will be removed from this queue and all preDeliveryQueues + * + * @param messageList + */ + public void removeMovedMessages(List messageList) + { + // Remove from the + boolean hasSubscribers = _subscriptions.hasActiveSubscribers(); + if (hasSubscribers) + { + for (Subscription sub : _subscriptions.getSubscriptions()) + { + if (!sub.isSuspended() && sub.filtersMessages()) + { + Queue preDeliveryQueue = sub.getPreDeliveryQueue(); + for (AMQMessage msg : messageList) + { + preDeliveryQueue.remove(msg); + } + } + } + } + + for (AMQMessage msg : messageList) + { + if (_messages.remove(msg)) + { + _totalMessageSize.getAndAdd(-msg.getSize()); + } + } + } + + /** + * Now with implementation of predelivery queues, this method will mark the message on the top as taken. + * + * @param storeContext + * + * @throws AMQException + */ + public void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException + { + _lock.lock(); + + AMQMessage message = _messages.poll(); + + if (message != null) + { + queue.dequeue(storeContext, message); + + _totalMessageSize.addAndGet(-message.getSize()); + + //If this causes ref count to hit zero then data will be purged so message.getSize() will NPE. + message.decrementReference(storeContext); + + } + + _lock.unlock(); + } + + public long clearAllMessages(StoreContext storeContext) throws AMQException + { + long count = 0; + _lock.lock(); + + synchronized (_queueHeadLock) + { + AMQMessage msg = getNextMessage(); + while (msg != null) + { + //and remove it + _messages.poll(); + + _queue.dequeue(storeContext, msg); + + msg.decrementReference(_reapingStoreContext); + + msg = getNextMessage(); + count++; + } + _totalMessageSize.set(0L); + } + _lock.unlock(); + return count; + } + + /** + * This can only be used to clear the _messages queue. Any subscriber resend queue will not be purged. + * + * @return the next message or null + * + * @throws org.apache.qpid.AMQException + */ + private AMQMessage getNextMessage() throws AMQException + { + return getNextMessage(_messages, null, false); + } + + private AMQMessage getNextMessage(Queue messages, Subscription sub, boolean purgeOnly) throws AMQException + { + AMQMessage message = messages.peek(); + + //while (we have a message) && ((The subscriber is not a browser or message is taken ) or we are clearing) && (Check message is taken.) + while (purgeMessage(message, sub, purgeOnly)) + { + // if we are purging then ensure we mark this message taken for the current subscriber + // the current subscriber may be null in the case of a get or a purge but this is ok. +// boolean alreadyTaken = message.taken(_queue, sub); + + //remove the already taken message or expired + AMQMessage removed = messages.poll(); + + assert removed == message; + + // if the message expired then the _totalMessageSize needs adjusting + if (message.expired(_queue) && !message.getDeliveredToConsumer()) + { + _totalMessageSize.addAndGet(-message.getSize()); + + // Use the reapingStoreContext as any sub(if we have one) may be in a tx. + _queue.dequeue(_reapingStoreContext, message); + + message.decrementReference(_reapingStoreContext); + + if (_log.isInfoEnabled()) + { + _log.info(debugIdentity() + " Doing clean up of the main _message queue."); + } + } + + //else the clean up is not required as the message has already been taken for this queue therefore + // it was the responsibility of the code that took the message to ensure the _totalMessageSize was updated. + + if (_log.isTraceEnabled()) + { + _log.trace("Removed taken message:" + message.debugIdentity()); + } + + // try the next message + message = messages.peek(); + } + + return message; + } + + /** + * This method will return true if the message is to be purged from the queue. + * + * + * SIDE-EFFECT: The message will be taken by the Subscription(sub) for the current Queue(_queue) + * + * @param message + * @param sub + * + * @return + * + * @throws AMQException + */ + private boolean purgeMessage(AMQMessage message, Subscription sub) throws AMQException + { + return purgeMessage(message, sub, false); + } + + /** + * This method will return true if the message is to be purged from the queue. + * \ + * SIDE-EFFECT: The msg will be taken by the Subscription(sub) for the current Queue(_queue) when purgeOnly is false + * + * @param message + * @param sub + * @param purgeOnly When set to false the message will be taken by the given Subscription. + * + * @return if the msg should be purged + * + * @throws AMQException + */ + private boolean purgeMessage(AMQMessage message, Subscription sub, boolean purgeOnly) throws AMQException + { + //Original.. complicated while loop control +// (message != null +// && ( +// ((sub != null && !sub.isBrowser()) || message.isTaken(_queue)) +// || sub == null) +// && message.taken(_queue, sub)); + + boolean purge = false; + + // if the message is null then don't purge as we have no messagse. + if (message != null) + { + // Check that the message hasn't expired. + if (message.expired(_queue)) + { + return true; + } + + // if we have a subscriber perform message checks + if (sub != null) + { + // if we have a queue browser(we don't purge) so check mark the message as taken + purge = ((!sub.isBrowser() || message.isTaken(_queue))); + } + else + { + // if there is no subscription we are doing + // a get or purging so mark message as taken. + message.isTaken(_queue); + // and then ensure that it gets purged + purge = true; + } + } + + if (purgeOnly) + { + // If we are simply purging the queue don't take the message + // just purge up to the next non-taken msg. + return purge && message.isTaken(_queue); + } + else + { + // if we are purging then ensure we mark this message taken for the current subscriber + // the current subscriber may be null in the case of a get or a purge but this is ok. + return purge && message.taken(_queue, sub); + } + } + + public void sendNextMessage(Subscription sub, AMQQueue queue)//Queue messageQueue) + { + + Queue messageQueue = sub.getNextQueue(_messages); + + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) + + ") from queue (" + System.identityHashCode(messageQueue) + + ") AMQQueue (" + System.identityHashCode(queue) + ")"); + } + + if (messageQueue == null) + { + // There is no queue with messages currently. This is ok... just means the queue has no msgs matching selector + if (_log.isInfoEnabled()) + { + _log.info(debugIdentity() + sub + ": asked to send messages but has none on given queue:" + queue); + } + return; + } + + AMQMessage message = null; + AMQMessage removed = null; + try + { + synchronized (_queueHeadLock) + { + message = getNextMessage(messageQueue, sub, false); + + // message will be null if we have no messages in the messageQueue. + if (message == null) + { + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")"); + } + return; + } + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Async Delivery Message :" + message + "(" + System.identityHashCode(message) + + ") by :" + System.identityHashCode(this) + + ") to :" + System.identityHashCode(sub)); + } + + + if (messageQueue == _messages) + { + _totalMessageSize.addAndGet(-message.getSize()); + } + + sub.send(message, _queue); + + //remove sent message from our queue. + removed = messageQueue.poll(); + //If we don't remove the message from _messages + // Otherwise the Async send will never end + } + + if (removed != message) + { + _log.error("Just send message:" + message.debugIdentity() + " BUT removed this from queue:" + removed); + } + + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Async Delivered Message r:" + removed.debugIdentity() + "d:" + message + + ") by :" + System.identityHashCode(this) + + ") to :" + System.identityHashCode(sub)); + } + + + if (messageQueue == sub.getResendQueue()) + { + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "All messages sent from resendQueue for " + sub); + } + if (messageQueue.isEmpty()) + { + subscriberHasPendingResend(false, sub, null); + //better to use the above method as this keeps all the tracking in one location. + // _hasContent.remove(sub); + } + + _extraMessages.decrementAndGet(); + } + else if (messageQueue == sub.getPreDeliveryQueue() && !sub.isBrowser()) + { + if (_log.isInfoEnabled()) + { + //fixme - we should do the clean up as the message remains on the _message queue + // this is resulting in the next consumer receiving the message and then attempting to purge it + // + cleanMainQueue(sub); + } + } + + } + catch (AMQException e) + { + if (message != null) + { + message.release(_queue); + } + else + { + _log.error(debugIdentity() + "Unable to release message as it is null. " + e, e); + } + _log.error(debugIdentity() + "Unable to deliver message as dequeue failed: " + e, e); + } + } + + private void cleanMainQueue(Subscription sub) + { + try + { + getNextMessage(_messages, sub, true); + } + catch (AMQException e) + { + _log.warn("Problem during main queue purge:" + e.getMessage()); + } + } + + /** + * enqueues the messages in the list on the queue and all required predelivery queues + * + * @param storeContext + * @param movedMessageList + */ + public void enqueueMovedMessages(StoreContext storeContext, List movedMessageList) + { + _lock.lock(); + for (AMQMessage msg : movedMessageList) + { + addMessageToQueue(msg, false); + } + + // enqueue on the pre delivery queues + for (Subscription sub : _subscriptions.getSubscriptions()) + { + for (AMQMessage msg : movedMessageList) + { + // Only give the message to those that want them. + if (sub.hasInterest(msg)) + { + sub.enqueueForPreDelivery(msg, true); + } + } + } + _lock.unlock(); + } + + /** + * Only one thread should ever execute this method concurrently, but it can do so while other threads invoke + * deliver(). + */ + private void processQueue() + { + //record thread name + if (_log.isDebugEnabled()) + { + _processingThreadName = Thread.currentThread().getName(); + } + + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Running process Queue." + currentStatus()); + } + + // Continue to process delivery while we haveSubscribers and messages + boolean hasSubscribers = _subscriptions.hasActiveSubscribers(); + + while (hasSubscribers && hasQueuedMessages() && !_movingMessages.get()) + { + hasSubscribers = false; + + for (Subscription sub : _subscriptions.getSubscriptions()) + { + synchronized (sub.getSendLock()) + { + if (!sub.isSuspended()) + { + sendNextMessage(sub, _queue); + + hasSubscribers = true; + } + } + } + } + + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Done process Queue." + currentStatus()); + } + + } + + public void deliver(StoreContext context, AMQShortString name, AMQMessage msg, boolean deliverFirst) throws AMQException + { + + final boolean debugEnabled = _log.isDebugEnabled(); + if (debugEnabled) + { + _log.debug(debugIdentity() + "deliver :first(" + deliverFirst + ") :" + msg); + } + + //Check if we have someone to deliver the message to. + _lock.lock(); + try + { + Subscription s = _subscriptions.nextSubscriber(msg); + + if (s == null || (!s.filtersMessages() && hasQueuedMessages())) //no-one can take the message right now or we're queueing + { + if (debugEnabled) + { + _log.debug(debugIdentity() + "Testing Message(" + msg + ") for Queued Delivery:" + currentStatus()); + } + if (!msg.getMessagePublishInfo().isImmediate()) + { + addMessageToQueue(msg, deliverFirst); + + //release lock now message is on queue. + _lock.unlock(); + + //Pre Deliver to all subscriptions + if (debugEnabled) + { + _log.debug(debugIdentity() + "We have " + _subscriptions.getSubscriptions().size() + + " subscribers to give the message to:" + currentStatus()); + } + for (Subscription sub : _subscriptions.getSubscriptions()) + { + + // stop if the message gets delivered whilst PreDelivering if we have a shared queue. + if (_queue.isShared() && msg.getDeliveredToConsumer()) + { + if (debugEnabled) + { + _log.debug(debugIdentity() + "Stopping PreDelivery as message(" + System.identityHashCode(msg) + + ") is already delivered."); + } + continue; + } + + // Only give the message to those that want them. + if (sub.hasInterest(msg)) + { + if (debugEnabled) + { + _log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(msg) + + ") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")"); + } + sub.enqueueForPreDelivery(msg, deliverFirst); + } + } + + //if we have a non-filtering subscriber but queued messages && we're not Async && we have other Active subs then something is wrong! + if ((s != null && hasQueuedMessages()) && !isProcessingAsync() && _subscriptions.hasActiveSubscribers()) + { + _queue.deliverAsync(); + } + + } + } + else + { + + if (s.filtersMessages()) + { + if (s.getPreDeliveryQueue().size() > 0) + { + _log.error("Direct delivery from PDQ with queued msgs:" + s.getPreDeliveryQueue().size()); + } + } + else if (_messages.size() > 0) + { + _log.error("Direct delivery from MainQueue queued msgs:" + _messages.size()); + } + + //release lock now + _lock.unlock(); + synchronized (s.getSendLock()) + { + if (!s.isSuspended()) + { + if (_log.isTraceEnabled()) + { + _log.trace(debugIdentity() + "Delivering Message:" + msg.debugIdentity() + " to(" + + System.identityHashCode(s) + ") :" + s); + } + + if (msg.taken(_queue, s)) + { + //Message has been delivered so don't redeliver. + // This can currently occur because of the recursive call below + // During unit tests the send can occur + // client then rejects + // this reject then releases the message by the time the + // if(!msg.isTaken()) call is made below + // the message has been released so that thread loops to send the message again + // of course by the time it gets back to here. the thread that released the + // message is now ready to send it. Here is a sample trace for reference +//1192627162613:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false] +//1192627162613:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null} +//1192627162613:Thread[pool-917-thread-4,5,main]:28398657 Sent :dt:214 msg:(HC:5529738 ID:145 Ref:1) +//1192627162613:Thread[pool-917-thread-2,5,main]:Reject message by:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false] +//1192627162613:Thread[pool-917-thread-2,5,main]:Releasing Message:(HC:5529738 ID:145 Ref:1) +//1192627162613:Thread[pool-917-thread-2,5,main]:Msg:Release:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:This:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]} +//1192627162613:Thread[pool-917-thread-2,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false] +//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:suspended: Message((HC:5529738 ID:145 Ref:1)) has not been taken so recursing!: Subscriber:28398657 +//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false] +//1192627162629:Thread[pool-917-thread-2,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null} +//1192627162629:Thread[pool-917-thread-2,5,main]:25386607 Sent :dt:172 msg:(HC:5529738 ID:145 Ref:1) +//1192627162629:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=true} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]} + // Note: In the last request to take the message from thread 4,5 the message has been + // taken by the previous call done by thread 2,5 + + + return; + } + //Deliver the message + s.send(msg, _queue); + } + else + { + if (debugEnabled) + { + _log.debug(debugIdentity() + " Subscription(" + System.identityHashCode(s) + ") became " + + "suspended between nextSubscriber and send for message:" + msg.debugIdentity()); + } + } + } + + // + // Why do we do this? What was the reasoning? We should have a better approach + // than recursion and rejecting if someone else sends it before we do. + // + if (!msg.isTaken(_queue)) + { + if (debugEnabled) + { + _log.debug(debugIdentity() + " Message(" + msg.debugIdentity() + ") has not been taken so recursing!:" + + " Subscriber:" + System.identityHashCode(s)); + } + + deliver(context, name, msg, deliverFirst); + } + else + { + if (debugEnabled) + { + _log.debug(debugIdentity() + " Message(" + msg.toString() + + ") has been taken so disregarding deliver request to Subscriber:" + + System.identityHashCode(s)); + } + } + } + + } + finally + { + //ensure lock is released + if (_lock.isHeldByCurrentThread()) + { + _lock.unlock(); + } + } + } + + private final String id = "(" + String.valueOf(System.identityHashCode(this)) + ")"; + + private String debugIdentity() + { + return id; + } + + final Runner _asyncDelivery = new Runner(); + + private class Runner implements Runnable + { + public void run() + { + String startName = Thread.currentThread().getName(); + Thread.currentThread().setName("CSDM-AsyncDelivery:" + startName); + boolean running = true; + while (running && !_movingMessages.get()) + { + processQueue(); + + //Check that messages have not been added since we did our last peek(); + // Synchronize with the thread that adds to the queue. + // If the queue is still empty then we can exit + synchronized (_asyncDelivery) + { + if (!(hasQueuedMessages() && _subscriptions.hasActiveSubscribers())) + { + running = false; + _processing.set(false); + } + } + } + Thread.currentThread().setName(startName); + } + } + + public void processAsync(Executor executor) + { + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Processing Async." + currentStatus()); + } + + synchronized (_asyncDelivery) + { + if (hasQueuedMessages() && _subscriptions.hasActiveSubscribers()) + { + //are we already running? if so, don't re-run + if (_processing.compareAndSet(false, true)) + { + if (_log.isDebugEnabled()) + { + _log.debug(debugIdentity() + "Executing Async process."); + } + executor.execute(_asyncDelivery); + } + } + } + } + + private String currentStatus() + { + return " Queued:" + (_messages.isEmpty() ? "Empty " : "Contains(H:M)") + + "(" + ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize() + + ":" + (_messages.size() - ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize()) + ") " + + " Extra: " + (_hasContent.isEmpty() ? "Empty " : "Contains") + + "(" + _hasContent.size() + ":" + _extraMessages.get() + ") " + + " Active:" + _subscriptions.hasActiveSubscribers() + + " Processing:" + (_processing.get() ? " true : Processing Thread: " + _processingThreadName : " false"); + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java new file mode 100644 index 0000000000..cbe9246f09 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java new file mode 100644 index 0000000000..153106d919 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.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.queue; + +import java.util.List; +import java.util.concurrent.Executor; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.StoreContext; + +interface DeliveryManager +{ + /** + * Determines whether there are queued messages. Sets _queueing to false if there are no queued messages. This needs + * to be atomic. + * + * @return true if there are queued messages + */ + boolean hasQueuedMessages(); + + /** + * This method should not be used to determin if there are messages in the queue. + * + * @return int The number of messages in the queue + * + * @use hasQueuedMessages() for all controls relating to having messages on the queue. + */ + int getQueueMessageCount(); + + /** + * Requests that the delivery manager start processing the queue asynchronously if there is work that can be done + * (i.e. there are messages queued up and subscribers that can receive them.

This should be called when + * subscribers are added, but only after the consume-ok message has been returned as message delivery may start + * immediately. It should also be called after unsuspending a client.

+ * + * @param executor the executor on which the delivery should take place + */ + void processAsync(Executor executor); + + /** + * Handles message delivery. The delivery manager is always in one of two modes; it is either queueing messages for + * asynchronous delivery or delivering directly. + * + * @param storeContext + * @param name the name of the entity on whose behalf we are delivering the message + * @param msg the message to deliver + * @param deliverFirst + * + * @throws org.apache.qpid.server.queue.FailedDequeueException + * if the message could not be dequeued + */ + void deliver(StoreContext storeContext, AMQShortString name, AMQMessage msg, boolean deliverFirst) throws FailedDequeueException, AMQException; + + void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException; + + long clearAllMessages(StoreContext storeContext) throws AMQException; + + void startMovingMessages(); + + void enqueueMovedMessages(StoreContext context, List messageList); + + void stopMovingMessages(); + + void removeMovedMessages(List messageListToRemove); + + List getMessages(); + + List getMessages(long fromMessageId, long toMessageId); + + void populatePreDeliveryQueue(Subscription subscription); + + boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException; + + long getTotalMessageSize(); + + long getOldestMessageArrival(); + + void subscriberHasPendingResend(boolean hasContent, Subscription subscription, AMQMessage msg); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java new file mode 100644 index 0000000000..60c1a8f574 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java @@ -0,0 +1,137 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.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 static final FieldTable EMPTY_ARGUMENTS = new FieldTable(); + + static class ExchangeBinding + { + private final Exchange _exchange; + private final AMQShortString _routingKey; + private final FieldTable _arguments; + + ExchangeBinding(AMQShortString routingKey, Exchange exchange) + { + this(routingKey, exchange, EMPTY_ARGUMENTS); + } + + ExchangeBinding(AMQShortString routingKey, Exchange exchange, FieldTable arguments) + { + _routingKey = 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 int hashCode() + { + return (_exchange == null ? 0 : _exchange.hashCode()) + + (_routingKey == null ? 0 : _routingKey.hashCode()) + + (_arguments == null ? 0 : _arguments.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) + && _arguments.equals(eb._arguments); + } + } + + 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 void remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange) + { + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/FailedDequeueException.java new file mode 100644 index 0000000000..6466e81dd2 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java new file mode 100644 index 0000000000..630186991b --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java @@ -0,0 +1,143 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.ContentBody; +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 = new LinkedList(); + + private boolean _redelivered; + + private long _arrivalTime; + + public InMemoryMessageHandle() + { + } + + public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException + { + return _contentHeaderBody; + } + + public int getBodyCount(StoreContext context, Long messageId) + { + return _contentBodies.size(); + } + + public long getBodySize(StoreContext context, Long messageId) throws AMQException + { + return getContentHeaderBody(context, messageId).bodySize; + } + + public ContentChunk getContentChunk(StoreContext context, Long messageId, 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, Long messageId, ContentChunk contentBody, boolean isLastContentBody) + throws AMQException + { + _contentBodies.add(contentBody); + } + + public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException + { + return _messagePublishInfo; + } + + public boolean isRedelivered() + { + return _redelivered; + } + + + public void setRedelivered(boolean redelivered) + { + _redelivered = redelivered; + } + + public boolean isPersistent(StoreContext context, Long messageId) throws AMQException + { + //todo remove literal values to a constant file such as AMQConstants in common + ContentHeaderBody chb = getContentHeaderBody(context, messageId); + return chb.properties instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2; + } + + /** + * This is called when all the content has been received. + * @param messagePublishInfo + * @param contentHeaderBody + * @throws AMQException + */ + public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo, + ContentHeaderBody contentHeaderBody) + throws AMQException + { + _messagePublishInfo = messagePublishInfo; + _contentHeaderBody = contentHeaderBody; + _arrivalTime = System.currentTimeMillis(); + } + + public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException + { + // NO OP + } + + public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + { + // NO OP + } + + public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + { + // NO OP + } + + public long getArrivalTime() + { + return _arrivalTime; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java new file mode 100644 index 0000000000..061ab56024 --- /dev/null +++ b/Final2/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 thr 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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageCleanupException.java new file mode 100644 index 0000000000..090096d3c3 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java new file mode 100644 index 0000000000..94ab935115 --- /dev/null +++ b/Final2/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(store); + } + else + { + return new InMemoryMessageHandle(); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/MessageMetaData.java new file mode 100644 index 0000000000..6118a4c11f --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NoConsumersException.java new file mode 100644 index 0000000000..d6fd1eec89 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/NotificationCheck.java new file mode 100644 index 0000000000..6b3d65661f --- /dev/null +++ b/Final2/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 = queue.getMessageCount(); + final long maximumMessageCount = queue.getMaximumMessageCount(); + if (maximumMessageCount!= 0 && msgCount >= 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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueNotificationListener.java new file mode 100644 index 0000000000..959ca03c80 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java new file mode 100644 index 0000000000..1210f0e97c --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java new file mode 100644 index 0000000000..77688f19be --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.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.queue; + +import java.util.Queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.AMQChannel; + +public interface Subscription +{ + void send(AMQMessage msg, AMQQueue queue) throws AMQException; + + boolean isSuspended(); + + void queueDeleted(AMQQueue queue) throws AMQException; + + boolean filtersMessages(); + + boolean hasInterest(AMQMessage msg); + + Queue getPreDeliveryQueue(); + + Queue getResendQueue(); + + Queue getNextQueue(Queue messages); + + void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst); + + boolean isAutoClose(); + + void close(); + + boolean isClosed(); + + boolean isBrowser(); + + boolean wouldSuspend(AMQMessage msg); + + void addToResendQueue(AMQMessage msg); + + Object getSendLock(); + + AMQChannel getChannel(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java new file mode 100644 index 0000000000..917f7c4e97 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.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.framing.FieldTable; +import org.apache.qpid.server.protocol.AMQProtocolSession; + +/** + * 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, AMQQueue queue) throws AMQException; + + + Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag) + throws AMQException; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java new file mode 100644 index 0000000000..1299c3a80c --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java @@ -0,0 +1,680 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.Queue; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +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.output.ProtocolOutputConverter; +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; +import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize; +import org.apache.qpid.util.MessageQueue; +import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize; + +/** + * 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 class SubscriptionImpl implements Subscription +{ + + private static final Logger _suspensionlogger = Logger.getLogger("Suspension"); + private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class); + + public final AMQChannel channel; + + public final AMQProtocolSession protocolSession; + + public final AMQShortString consumerTag; + + private final Object _sessionKey; + + private MessageQueue _messages; + + private Queue _resendQueue; + + private final boolean _noLocal; + + /** True if messages need to be acknowledged */ + private final boolean _acks; + private FilterManager _filters; + private final boolean _isBrowser; + private final Boolean _autoClose; + private boolean _sentClose = false; + + private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString(); + + private AMQQueue _queue; + private final AtomicBoolean _sendLock = new AtomicBoolean(false); + + + public static class Factory implements SubscriptionFactory + { + public Subscription createSubscription(int channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, boolean acks, FieldTable filters, + boolean noLocal, AMQQueue queue) throws AMQException + { + return new SubscriptionImpl(channel, protocolSession, consumerTag, acks, filters, noLocal, queue); + } + + public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag) + throws AMQException + { + return new SubscriptionImpl(channel, protocolSession, consumerTag, false, null, false, null); + } + } + + public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession, + AMQShortString consumerTag, boolean acks) + throws AMQException + { + this(channelId, protocolSession, consumerTag, acks, null, false, null); + } + + public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession, + AMQShortString consumerTag, boolean acks, FieldTable filters, + boolean noLocal, AMQQueue queue) + throws AMQException + { + AMQChannel channel = protocolSession.getChannel(channelId); + if (channel == null) + { + throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); + } + + this.channel = channel; + this.protocolSession = protocolSession; + this.consumerTag = consumerTag; + _sessionKey = protocolSession.getKey(); + _acks = acks; + _noLocal = noLocal; + _queue = queue; + + _filters = FilterManagerFactory.createManager(filters); + + + if (_filters != null) + { + Object isBrowser = filters.get(AMQPFilterTypes.NO_CONSUME.getValue()); + if (isBrowser != null) + { + _isBrowser = (Boolean) isBrowser; + } + else + { + _isBrowser = false; + } + } + else + { + _isBrowser = false; + } + + + if (_filters != null) + { + Object autoClose = filters.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); + if (autoClose != null) + { + _autoClose = (Boolean) autoClose; + } + else + { + _autoClose = false; + } + } + else + { + _autoClose = false; + } + + + if (filtersMessages()) + { + _messages = new ConcurrentLinkedMessageQueueAtomicSize(); + } + else + { + // Reference the DeliveryManager + _messages = null; + } + } + + + public SubscriptionImpl(int channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag) + throws AMQException + { + this(channel, protocolSession, consumerTag, false); + } + + public boolean equals(Object o) + { + return (o instanceof SubscriptionImpl) && equals((SubscriptionImpl) o); + } + + /** + * Equality holds if the session matches and the channel and consumer tag are the same. + * + * @param psc The subscriptionImpl to compare + * + * @return equality + */ + private boolean equals(SubscriptionImpl psc) + { + return _sessionKey.equals(psc._sessionKey) + && psc.channel == channel + && psc.consumerTag.equals(consumerTag); + } + + public int hashCode() + { + return _sessionKey.hashCode(); + } + + public String toString() + { + String subscriber = "[channel=" + channel + + ", consumerTag=" + consumerTag + + ", session=" + protocolSession.getKey() + + ", resendQueue=" + (_resendQueue != null); + + if (_resendQueue != null) + { + subscriber += ", resendSize=" + _resendQueue.size(); + } + + + 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 + * @param queue the Queue it has been sent from + * + * @throws AMQException + */ + public void send(AMQMessage msg, AMQQueue queue) throws AMQException + { + if (msg != null) + { + if (_isBrowser) + { + sendToBrowser(msg, queue); + } + else + { + sendToConsumer(channel.getStoreContext(), msg, queue); + } + } + else + { + _logger.error("Attempt to send Null message", new NullPointerException()); + } + } + + private void sendToBrowser(AMQMessage msg, AMQQueue queue) 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 (channel) + { + long deliveryTag = channel.getNextDeliveryTag(); + + // We don't need to add the message to the unacknowledgedMap as we don't need to know if the client + // received the message. If it is lost in transit that is not important. +// if (_acks) +// { +// channel.addUnacknowledgedBrowsedMessage(msg, deliveryTag, consumerTag, queue); +// } + + if (_sendLock.get()) + { + _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!"); + } + + protocolSession.getProtocolOutputConverter().writeDeliver(msg, channel.getChannelId(), deliveryTag, consumerTag); + } + } + + private void sendToConsumer(StoreContext storeContext, AMQMessage msg, AMQQueue queue) + 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. + if (!_acks) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("No ack mode so dequeuing message immediately: " + msg.getMessageId()); + } + queue.dequeue(storeContext, msg); + } + + synchronized (channel) + { + long deliveryTag = channel.getNextDeliveryTag(); + + if (_sendLock.get()) + { + _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!"); + } + + if (_acks) + { + channel.addUnacknowledgedMessage(msg, deliveryTag, consumerTag, queue); + } + + protocolSession.getProtocolOutputConverter().writeDeliver(msg, channel.getChannelId(), deliveryTag, consumerTag); + + if (!_acks) + { + msg.decrementReference(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? + + msg.setDeliveredToConsumer(); + } + } + + public boolean isSuspended() + { +// if (_suspensionlogger.isInfoEnabled()) +// { +// if (channel.isSuspended()) +// { +// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") channel's is susupended"); +// } +// if (_sendLock.get()) +// { +// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") has sendLock set so closing."); +// } +// } + return channel.isSuspended() || _sendLock.get(); + } + + /** + * Callback indicating that a queue has been deleted. + * + * @param queue The queue to delete + */ + public void queueDeleted(AMQQueue queue) throws AMQException + { + channel.queueDeleted(queue); + } + + public boolean filtersMessages() + { + return _filters != null || _noLocal; + } + + public boolean hasInterest(AMQMessage msg) + { + //check that the message hasn't been rejected + if (msg.isRejectedBy(this)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + msg.debugIdentity()); + } +// return false; + } + + final AMQProtocolSession publisher = msg.getPublisher(); + + //todo - client id should be recoreded and this test removed but handled below + if (_noLocal && publisher != null) + { + // We don't want local messages so check to see if message is one we sent + Object localInstance; + Object msgInstance; + + if ((protocolSession.getClientProperties() != null) && + (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) + { + + if ((publisher.getClientProperties() != null) && + (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null) + { + if (localInstance == msgInstance || localInstance.equals(msgInstance)) + { +// if (_logger.isTraceEnabled()) +// { +// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" + +// msg.debugIdentity() + ")"); +// } + return false; + } + } + } + else + { + + localInstance = protocolSession.getClientIdentifier(); + //todo - client id should be recoreded and this test removed but handled here + + msgInstance = publisher.getClientIdentifier(); + if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance))) + { +// if (_logger.isTraceEnabled()) +// { +// _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" + +// msg.debugIdentity() + ")"); +// } + return false; + } + } + + + } + + + if (_logger.isTraceEnabled()) + { + _logger.trace("(" + debugIdentity() + ") checking filters for message (" + msg.debugIdentity()); + } + return checkFilters(msg); + + } + + private String id = String.valueOf(System.identityHashCode(this)); + + private String debugIdentity() + { + return id; + } + + private boolean checkFilters(AMQMessage msg) + { + if (_filters != null) + { +// if (_logger.isTraceEnabled()) +// { +// _logger.trace("(" + debugIdentity() + ") has filters."); +// } + return _filters.allAllow(msg); + } + else + { +// if (_logger.isTraceEnabled()) +// { +// _logger.trace("(" + debugIdentity() + ") has no filters"); +// } + + return true; + } + } + + public Queue getPreDeliveryQueue() + { + return _messages; + } + + public void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst) + { + if (_messages != null) + { + if (deliverFirst) + { + _messages.pushHead(msg); + } + else + { + _messages.offer(msg); + } + } + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public void close() + { + boolean closed = false; + synchronized (_sendLock) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Setting SendLock true:" + debugIdentity()); + } + + closed = _sendLock.getAndSet(true); + } + + if (closed) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Called close() on a closed subscription"); + } + + return; + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing subscription (" + debugIdentity() + "):" + this); + } + + if (_resendQueue != null && !_resendQueue.isEmpty()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Requeuing closing subscription (" + debugIdentity() + "):" + this); + } + requeue(); + } + + //remove references in PDQ + if (_messages != null) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Clearing PDQ (" + debugIdentity() + "):" + this); + } + + _messages.clear(); + } + } + + private void autoclose() + { + close(); + + if (_autoClose && !_sentClose) + { + _logger.info("Closing autoclose subscription (" + debugIdentity() + "):" + this); + + ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag); + _sentClose = true; + + //fixme JIRA do this better + try + { + channel.unsubscribeConsumer(protocolSession, consumerTag); + } + catch (AMQException e) + { + // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag. + } + } + } + + private void requeue() + { + if (_queue != null) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Requeuing :" + _resendQueue.size() + " messages"); + } + + while (!_resendQueue.isEmpty()) + { + AMQMessage resent = _resendQueue.poll(); + + if (_logger.isTraceEnabled()) + { + _logger.trace("Removed for resending:" + resent.debugIdentity()); + } + + resent.release(_queue); + _queue.subscriberHasPendingResend(false, this, resent); + + try + { + channel.getTransactionalContext().deliver(resent, _queue, true); + } + catch (AMQException e) + { + _logger.error("MESSAGE LOSS : Unable to re-deliver messages", e); + } + } + + if (!_resendQueue.isEmpty()) + { + _logger.error("[MESSAGES LOST]Unable to re-deliver messages as queue is null."); + } + + _queue.subscriberHasPendingResend(false, this, null); + } + else + { + if (!_resendQueue.isEmpty()) + { + _logger.error("Unable to re-deliver messages as queue is null."); + } + } + + // Clear the messages + _resendQueue = null; + } + + + public boolean isClosed() + { + return _sendLock.get(); // This rather than _close is used to signify the subscriber is now closed. + } + + public boolean isBrowser() + { + return _isBrowser; + } + + public boolean wouldSuspend(AMQMessage msg) + { + return channel.wouldSuspend(msg); + } + + public Queue getResendQueue() + { + if (_resendQueue == null) + { + _resendQueue = new ConcurrentLinkedQueueAtomicSize(); + } + return _resendQueue; + } + + + public Queue getNextQueue(Queue messages) + { + if (_resendQueue != null && !_resendQueue.isEmpty()) + { + return _resendQueue; + } + + if (filtersMessages()) + { + if (isAutoClose()) + { + if (_messages.isEmpty()) + { + autoclose(); + return null; + } + } + return _messages; + } + else // we want the DM queue + { + return messages; + } + } + + public void addToResendQueue(AMQMessage msg) + { + // add to our resend queue + getResendQueue().add(msg); + + // Mark Queue has having content. + if (_queue == null) + { + _logger.error("Queue is null won't be able to resend messages"); + } + else + { + _queue.subscriberHasPendingResend(true, this, msg); + } + } + + public Object getSendLock() + { + return _sendLock; + } + + public AMQChannel getChannel() + { + return channel; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java new file mode 100644 index 0000000000..4df88baebc --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.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; + +import java.util.List; + +/** + * Abstraction of actor that will determine the subscriber to whom + * a message will be sent. + */ +public interface SubscriptionManager +{ + public List getSubscriptions(); + public boolean hasActiveSubscribers(); + public Subscription nextSubscriber(AMQMessage msg); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java new file mode 100644 index 0000000000..b500247fa4 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java @@ -0,0 +1,229 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; + +/** Holds a set of subscriptions for a queue and manages the round robin-ing of deliver etc. */ +class SubscriptionSet implements WeightedSubscriptionManager +{ + private static final Logger _log = Logger.getLogger(SubscriptionSet.class); + + /** List of registered subscribers */ + private List _subscriptions = new CopyOnWriteArrayList(); + + /** Used to control the round robin delivery of content */ + private int _currentSubscriber; + private final Object _subscriptionsChange = new Object(); + + + /** Accessor for unit tests. */ + int getCurrentSubscriber() + { + return _currentSubscriber; + } + + public void addSubscriber(Subscription subscription) + { + synchronized (_subscriptionsChange) + { + _subscriptions.add(subscription); + } + } + + /** + * Remove the subscription, returning it if it was found + * + * @param subscription + * + * @return null if no match was found + */ + public Subscription removeSubscriber(Subscription subscription) + { + // TODO: possibly need O(1) operation here. + + Subscription sub = null; + synchronized (_subscriptionsChange) + { + int subIndex = _subscriptions.indexOf(subscription); + + if (subIndex != -1) + { + //we can't just return the passed in subscription as it is a new object + // and doesn't contain the stored state we need. + //NOTE while this may be removed now anyone with an iterator will still have it in the list!! + sub = _subscriptions.remove(subIndex); + } + else + { + _log.error("Unable to remove from index(" + subIndex + ")subscription:" + subscription); + } + } + if (sub != null) + { + return sub; + } + else + { + debugDumpSubscription(subscription); + return null; + } + } + + private void debugDumpSubscription(Subscription subscription) + { + if (_log.isDebugEnabled()) + { + _log.debug("Subscription " + subscription + " not found. Dumping subscriptions:"); + for (Subscription s : _subscriptions) + { + _log.debug("Subscription: " + s); + } + _log.debug("Subscription dump complete"); + } + } + + /** + * Return the next unsuspended subscription or null if not found.

Performance note: This method can scan all + * items twice when looking for a subscription that is not suspended. The worst case occcurs when all subscriptions + * are suspended. However, it is does this without synchronisation and subscriptions may be added and removed + * concurrently. Also note that because of race conditions and when subscriptions are removed between calls to + * nextSubscriber, the IndexOutOfBoundsException also causes the scan to start at the beginning. + */ + public Subscription nextSubscriber(AMQMessage msg) + { + if (_subscriptions.isEmpty()) + { + return null; + } + + try + { + final Subscription result = nextSubscriberImpl(msg); + if (result == null) + { + _currentSubscriber = 0; + return nextSubscriberImpl(msg); + } + else + { + return result; + } + } + catch (IndexOutOfBoundsException e) + { + _currentSubscriber = 0; + return nextSubscriber(msg); + } + } + + private Subscription nextSubscriberImpl(AMQMessage msg) + { + final ListIterator iterator = _subscriptions.listIterator(_currentSubscriber); + while (iterator.hasNext()) + { + Subscription subscription = iterator.next(); + ++_currentSubscriber; + subscriberScanned(); + + if (!(subscription.isSuspended() || subscription.wouldSuspend(msg))) + { + if (subscription.hasInterest(msg)) + { + // if the queue is not empty then this client is ready to receive a message. + //FIXME the queue could be full of sent messages. + // Either need to clean all PDQs after sending a message + // OR have a clean up thread that runs the PDQs expunging the messages. + if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty()) + { + return subscription; + } + } + } + } + + return null; + } + + /** Overridden in test classes. */ + protected void subscriberScanned() + { + } + + public boolean isEmpty() + { + return _subscriptions.isEmpty(); + } + + public List getSubscriptions() + { + return _subscriptions; + } + + public boolean hasActiveSubscribers() + { + for (Subscription s : _subscriptions) + { + if (!s.isSuspended()) + { + return true; + } + } + return false; + } + + public int getWeight() + { + int count = 0; + for (Subscription s : _subscriptions) + { + if (!s.isSuspended()) + { + count++; + } + } + return count; + } + + /** + * Notification that a queue has been deleted. This is called so that the subscription can inform the channel, which + * in turn can update its list of unacknowledged messages. + * + * @param queue + */ + public void queueDeleted(AMQQueue queue) throws AMQException + { + for (Subscription s : _subscriptions) + { + s.queueDeleted(queue); + } + } + + int size() + { + return _subscriptions.size(); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.java new file mode 100644 index 0000000000..79ee6b93a3 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/TransientMessageData.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.server.queue; + +import java.util.LinkedList; +import java.util.List; + +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 = new LinkedList(); + + public MessagePublishInfo getMessagePublishInfo() + { + return _messagePublishInfo; + } + + public void setMessagePublishInfo(MessagePublishInfo messagePublishInfo) + { + _messagePublishInfo = messagePublishInfo; + } + + public List getDestinationQueues() + { + return _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) + { + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java new file mode 100644 index 0000000000..373a64e2eb --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java @@ -0,0 +1,227 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.BasicContentHeaderProperties; +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 long _arrivalTime; + + + public WeakReferenceMessageHandle(MessageStore messageStore) + { + _messageStore = messageStore; + } + + public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException + { + ContentHeaderBody chb = (_contentHeaderBody != null ? _contentHeaderBody.get() : null); + if (chb == null) + { + MessageMetaData mmd = loadMessageMetaData(context, messageId); + chb = mmd.getContentHeaderBody(); + } + return chb; + } + + private MessageMetaData loadMessageMetaData(StoreContext context, Long messageId) + 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, Long messageId) 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, Long messageId) throws AMQException + { + return getContentHeaderBody(context, messageId).bodySize; + } + + public ContentChunk getContentChunk(StoreContext context, Long messageId, 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 messageId + * @param contentChunk + * @param isLastContentBody + * @throws AMQException + */ + public void addContentBodyFrame(StoreContext storeContext, Long messageId, 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, Long messageId) throws AMQException + { + MessagePublishInfo bpb = (_messagePublishInfo != null ? _messagePublishInfo.get() : null); + if (bpb == null) + { + MessageMetaData mmd = loadMessageMetaData(context, messageId); + + bpb = mmd.getMessagePublishInfo(); + } + return bpb; + } + + public boolean isRedelivered() + { + return _redelivered; + } + + public void setRedelivered(boolean redelivered) + { + _redelivered = redelivered; + } + + public boolean isPersistent(StoreContext context, Long messageId) throws AMQException + { + //todo remove literal values to a constant file such as AMQConstants in common + ContentHeaderBody chb = getContentHeaderBody(context, messageId); + return chb.properties instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2; + } + + /** + * This is called when all the content has been received. + * + * @param publishBody + * @param contentHeaderBody + * @throws AMQException + */ + public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, 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, Long messageId) throws AMQException + { + _messageStore.removeMessage(storeContext, messageId); + } + + public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + { + _messageStore.enqueueMessage(storeContext, queue.getName(), messageId); + } + + public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException + { + _messageStore.dequeueMessage(storeContext, queue.getName(), messageId); + } + + public long getArrivalTime() + { + return _arrivalTime; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java new file mode 100644 index 0000000000..6c71571807 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.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; + +public interface WeightedSubscriptionManager extends SubscriptionManager +{ + public int getWeight(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java new file mode 100644 index 0000000000..22fa0fab23 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.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.registry; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +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; + +/** + * 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 +{ + private 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; + + static + { + Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService())); + } + + private static class ShutdownService implements Runnable + { + public void run() + { + _logger.info("Shutting down application registries..."); + try + { + synchronized (ApplicationRegistry.class) + { + Iterator keyIterator = _instanceMap.values().iterator(); + + while (keyIterator.hasNext()) + { + IApplicationRegistry instance = keyIterator.next(); + + instance.close(); + } + } + } + catch (Exception e) + { + _logger.error("Error shutting down message store: " + e, e); + } + } + } + + 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); + } + } + + public static void remove(int instanceID) + { + try + { + _instanceMap.get(instanceID).close(); + } + catch (Exception e) + { + + } + finally + { + _instanceMap.remove(instanceID); + } + } + + + 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 + { + for(VirtualHost virtualHost : getVirtualHostRegistry().getVirtualHosts()) + { + virtualHost.close(); + } + + // close the rmi registry(if any) started for management + if (getInstance().getManagedObjectRegistry() != null) + { + getInstance().getManagedObjectRegistry().close(); + } + } + + public Configuration getConfiguration() + { + return _configuration; + } + + 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; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java new file mode 100644 index 0000000000..1cca259a8d --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.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.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.AccessManager; +import org.apache.qpid.server.security.access.AccessManagerImpl; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.AMQException; + +public class ConfigurationFileApplicationRegistry extends ApplicationRegistry +{ + + private ManagedObjectRegistry _managedObjectRegistry; + + private AuthenticationManager _authenticationManager; + + private AccessManager _accessManager; + + private PrincipalDatabaseManager _databaseManager; + + private VirtualHostRegistry _virtualHostRegistry; + + + private final Map _virtualHosts = new ConcurrentHashMap(); + + + 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 = new AccessManagerImpl("default", _configuration); + + _databaseManager = new ConfigurationFilePrincipalDatabaseManager(); + + _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); + + _databaseManager.initialiseManagement(_configuration); + + _managedObjectRegistry.start(); + + 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 VirtualHostRegistry getVirtualHostRegistry() + { + return _virtualHostRegistry; + } + + public AccessManager getAccessManager() + { + return _accessManager; + } + + public ManagedObjectRegistry getManagedObjectRegistry() + { + return _managedObjectRegistry; + } + + public PrincipalDatabaseManager getDatabaseManager() + { + return _databaseManager; + } + + public AuthenticationManager getAuthenticationManager() + { + return _authenticationManager; + } + + public Collection getVirtualHostNames() + { + return getConfiguration().getList("virtualhosts.virtualhost.name"); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java new file mode 100644 index 0000000000..5a48431288 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.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.registry; + +import java.util.Collection; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.server.management.ManagedObjectRegistry; +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.AccessManager; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +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; + + 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(); + + AccessManager getAccessManager(); +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java new file mode 100644 index 0000000000..2dc7fcbc1e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AMQUserManagementMBean.java @@ -0,0 +1,467 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.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.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); + + // 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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.java new file mode 100644 index 0000000000..d70a6dc8f4 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManager.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.access; + +import java.security.Principal; + +public interface AccessManager +{ + AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights); + + @Deprecated + AccessResult isAuthorized(Accessable accessObject, String username); + + String getName(); + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java new file mode 100644 index 0000000000..35d036d20f --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessManagerImpl.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.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.auth.sasl.UsernamePrincipal; +import org.apache.qpid.configuration.PropertyUtils; +import org.apache.log4j.Logger; + +import java.util.List; +import java.lang.reflect.Method; +import java.security.Principal; + +public class AccessManagerImpl implements AccessManager +{ + private static final Logger _logger = Logger.getLogger(AccessManagerImpl.class); + + AccessManager _accessManager; + + public AccessManagerImpl(String name, Configuration hostConfig) throws ConfigurationException + { + if (hostConfig == null) + { + _logger.warn("No Configuration specified. Using default access controls for VirtualHost:'" + name + "'"); + return; + } + + String accessClass = hostConfig.getString("security.access.class"); + if (accessClass == null) + { + _logger.warn("No access control specified. Using default access controls for VirtualHost:'" + name + "'"); + return; + } + + Object o; + try + { + o = Class.forName(accessClass).newInstance(); + } + catch (Exception e) + { + throw new ConfigurationException("Error initialising access control: " + e, e); + } + + if (!(o instanceof AccessManager)) + { + throw new ConfigurationException("Access control must implement the VirtualHostAccess interface"); + } + + initialiseAccessControl((AccessManager) o, hostConfig); + + _accessManager = (AccessManager) o; + + _logger.info("Initialised access control for virtualhost '" + name + "' successfully"); + + } + + + private void initialiseAccessControl(AccessManager accessManager, Configuration config) + throws ConfigurationException + { + 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; + } + } + } + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ); + } + + public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights) + { + if (_accessManager == null) + { + if (ApplicationRegistry.getInstance().getAccessManager() == this) + { + _logger.warn("No Default access manager specified DENYING ALL ACCESS"); + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + else + { + return ApplicationRegistry.getInstance().getAccessManager().isAuthorized(accessObject, user, rights); + } + } + else + { + return _accessManager.isAuthorized(accessObject, user, rights); + } + } + + public String getName() + { + return "AccessManagerImpl"; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.java new file mode 100644 index 0000000000..b8d8fc605a --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessResult.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.access; + +public class AccessResult +{ + public enum AccessStatus + { + GRANTED, REFUSED + } + + StringBuilder _authorizer; + AccessStatus _status; + + public AccessResult(AccessManager authorizer, AccessStatus status) + { + _status = status; + _authorizer = new StringBuilder(authorizer.getName()); + } + + public void setAuthorizer(AccessManager authorizer) + { + _authorizer.append(authorizer.getName()); + } + + public String getAuthorizer() + { + return _authorizer.toString(); + } + + public void setStatus(AccessStatus status) + { + _status = status; + } + + public AccessStatus getStatus() + { + return _status; + } + + public void addAuthorizer(AccessManager accessManager) + { + _authorizer.insert(0, "->"); + _authorizer.insert(0, accessManager.getName()); + } + + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AccessRights.java new file mode 100644 index 0000000000..1b79a5a0e0 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/Accessable.java new file mode 100644 index 0000000000..f51cf24caa --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.java new file mode 100644 index 0000000000..1ddca3a64e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/AllowAll.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.security.access; + +import java.security.Principal; + +public class AllowAll implements AccessManager +{ + + public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights) + { + return new AccessResult(this, AccessResult.AccessStatus.GRANTED); + } + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return new AccessResult(this, AccessResult.AccessStatus.GRANTED); + } + + public String getName() + { + return "AllowAll"; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.java new file mode 100644 index 0000000000..bf40eeba4e --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/DenyAll.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.security.access; + +import java.security.Principal; + +public class DenyAll implements AccessManager +{ + public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights) + { + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + + public String getName() + { + return "DenyAll"; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java new file mode 100644 index 0000000000..291bc714ed --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessManager.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.access; + +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.log4j.Logger; + +import java.io.IOException; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.File; +import java.util.regex.Pattern; +import java.security.Principal; + +/** + * 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 FileAccessManager implements AccessManager +{ + private static final Logger _logger = Logger.getLogger(FileAccessManager.class); + + protected File _accessFile; + + protected Pattern _regexp = Pattern.compile(":"); + + private static final short USER_INDEX = 0; + private static final short VIRTUALHOST_INDEX = 1; + + public void setAccessFile(String accessFile) throws FileNotFoundException + { + File f = new File(accessFile); + _logger.info("FileAccessManager using file " + f.getAbsolutePath()); + _accessFile = f; + if (!f.exists()) + { + throw new FileNotFoundException("Cannot find access file " + f); + } + if (!f.canRead()) + { + throw new FileNotFoundException("Cannot read access file " + f + + ". Check permissions."); + } + } + + /** + * Looks up the virtual hosts for a specified user in the access file. + * + * @param user The user to lookup + * + * @return a list of virtualhosts + */ + private VirtualHostAccess[] lookupVirtualHost(String user) + { + String[] results = lookup(user, VIRTUALHOST_INDEX); + VirtualHostAccess vhosts[] = new VirtualHostAccess[results.length]; + + for (int index = 0; index < results.length; index++) + { + vhosts[index] = new VirtualHostAccess(results[index]); + } + + return vhosts; + } + + + private String[] lookup(String user, int index) + { + try + { + BufferedReader reader = null; + try + { + reader = new BufferedReader(new FileReader(_accessFile)); + String line; + + while ((line = reader.readLine()) != null) + { + String[] result = _regexp.split(line); + if (result == null || result.length < (index + 1)) + { + continue; + } + + if (user.equals(result[USER_INDEX])) + { + return result[index].split(","); + } + } + return null; + } + finally + { + if (reader != null) + { + reader.close(); + } + } + } + catch (IOException ioe) + { + //ignore + } + return null; + } + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ); + } + + public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights) + { + if (accessObject instanceof VirtualHost) + { + VirtualHostAccess[] hosts = lookupVirtualHost(user.getName()); + + if (hosts != null) + { + for (VirtualHostAccess host : hosts) + { + if (accessObject.getAccessableName().equals(host.getVirtualHost())) + { + if (host.getAccessRights().allows(rights)) + { + return new AccessResult(this, AccessResult.AccessStatus.GRANTED); + } + else + { + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + } + } + } + } +// else if (accessObject instanceof AMQQueue) +// { +// String[] queues = lookupQueue(username, ((AMQQueue) accessObject).getVirtualHost()); +// +// if (queues != null) +// { +// for (String queue : queues) +// { +// if (accessObject.getAccessableName().equals(queue)) +// { +// return new AccessResult(this, AccessResult.AccessStatus.GRANTED); +// } +// } +// } +// } + + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + + public String getName() + { + return "FileAccessManager"; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.java new file mode 100644 index 0000000000..6ccadb2e7d --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/PrincipalDatabaseAccessManager.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.security.access; + +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.log4j.Logger; + +import java.security.Principal; + +public class PrincipalDatabaseAccessManager implements AccessManager +{ + private static final Logger _logger = Logger.getLogger(PrincipalDatabaseAccessManager.class); + + PrincipalDatabase _database; + AccessManager _default; + + public PrincipalDatabaseAccessManager() + { + _default = null; + } + + public void setDefaultAccessManager(String defaultAM) + { + if (defaultAM.equals("AllowAll")) + { + _default = new AllowAll(); + } + + if (defaultAM.equals("DenyAll")) + { + _default = new DenyAll(); + } + } + + public void setPrincipalDatabase(String database) + { + _database = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().get(database); + if (!(_database instanceof AccessManager)) + { + _logger.warn("Database '" + database + "' cannot perform access management"); + } + } + + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ); + } + + public AccessResult isAuthorized(Accessable accessObject, Principal username, AccessRights.Rights rights) + { + AccessResult result; + + if (_database == null) + { + if (_default != null) + { + result = _default.isAuthorized(accessObject, username, rights); + } + else + { + throw new RuntimeException("Principal Database and default Access Manager are both null unable to perform Access Control"); + } + } + else + { + if (!(_database instanceof AccessManager)) + { + _logger.warn("Specified PrincipalDatabase is not an AccessManager so using default AccessManager"); + result = _default.isAuthorized(accessObject, username, rights); + } + else + { + result = ((AccessManager) _database).isAuthorized(accessObject, username, rights); + } + } + + result.addAuthorizer(this); + + return result; + } + + public String getName() + { + return "PrincipalDatabaseFileAccessManager"; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/UserManagement.java new file mode 100644 index 0000000000..b8762aa43b --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/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; + +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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/access/VirtualHostAccess.java new file mode 100644 index 0000000000..13151a66b8 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java new file mode 100644 index 0000000000..0e3aea4de0 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.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; + +public class AuthenticationResult +{ + public enum AuthenticationStatus + { + SUCCESS, CONTINUE, ERROR + } + + public AuthenticationStatus status; + public byte[] challenge; + + public AuthenticationResult(byte[] challenge, AuthenticationStatus status) + { + this.status = status; + this.challenge = challenge; + } + + public AuthenticationResult(AuthenticationStatus status) + { + this.status = status; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java new file mode 100644 index 0000000000..10adfdd9fc --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabase.java @@ -0,0 +1,599 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.crammd5.CRAMMD5HashedInitialiser; +import org.apache.qpid.server.security.access.AMQUserManagementMBean; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.EncoderException; + +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.AccountNotFoundException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.UnsupportedEncodingException; +import java.io.PrintStream; +import java.util.regex.Pattern; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.LinkedList; +import java.util.concurrent.locks.ReentrantLock; +import java.security.Principal; +import java.security.NoSuchAlgorithmException; +import java.security.MessageDigest; + +/** + * 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; + private 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 + * + * @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); + + int index = 0; + boolean verified = true; + + while (verified & index < password.length) + { + verified = (pwd[index] == password[index]); + index++; + } + return verified; + } + + public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException + { + User 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; + } + + User user = new User(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 + { + User 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) + { + User 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; + } + + User user = new User(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)); + continue; + } + + User 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.getEncodePassword(); + + 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 (User user : _users.values()) + { + if (user.isModified()) + { + byte[] encodedPassword; + try + { + encodedPassword = user.getEncodePassword(); + 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(); + } + } + } + + private class User implements Principal + { + String _name; + char[] _password; + byte[] _encodedPassword = null; + private boolean _modified = false; + private boolean _deleted = false; + + User(String[] data) throws UnsupportedEncodingException + { + if (data.length != 2) + { + throw new IllegalArgumentException("User Data should be lenght 2, username, password"); + } + + _name = data[0]; + + byte[] encoded_password = data[1].getBytes(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 User(String name, char[] password) + { + _name = name; + setPassword(password); + } + + public String getName() + { + return _name; + } + + public String toString() + { + if (_logger.isDebugEnabled()) + { + return getName() + ((_encodedPassword == null) ? "" : ":" + new String(_encodedPassword)); + } + else + { + return _name; + } + } + + char[] getPassword() + { + return _password; + } + + void setPassword(char[] password) + { + _password = password; + _modified = true; + _encodedPassword = null; + } + + + byte[] getEncodePassword() 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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java new file mode 100644 index 0000000000..2d3f5e5131 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.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.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.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() throws Exception + { + _logger.info("Initialising PrincipleDatabase authentication manager"); + _databases = initialisePrincipalDatabases(); + } + + private Map initialisePrincipalDatabases() throws Exception + { + Configuration config = ApplicationRegistry.getInstance().getConfiguration(); + List databaseNames = config.getList(_base + ".name"); + List databaseClasses = config.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, config, 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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabase.java new file mode 100644 index 0000000000..352d41a0ba --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java new file mode 100644 index 0000000000..5c372f6c2c --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PlainPasswordVhostFilePrincipalDatabase.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.AccessManager; +import org.apache.qpid.server.security.access.AccessResult; +import org.apache.qpid.server.security.access.AccessRights; +import org.apache.qpid.server.security.access.Accessable; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.security.Principal; + +/** + * 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 PlainPasswordVhostFilePrincipalDatabase extends PlainPasswordFilePrincipalDatabase implements AccessManager +{ + private static final Logger _logger = Logger.getLogger(PlainPasswordVhostFilePrincipalDatabase.class); + + /** + * Looks up the virtual hosts for a specified user in the password file. + * + * @param user The user to lookup + * + * @return a list of virtualhosts + */ + private String[] lookupVirtualHost(String user) + { + try + { + 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 < 3) + { + continue; + } + + if (user.equals(result[0])) + { + return result[2].split(","); + } + } + } + return null; + } + finally + { + if (reader != null) + { + reader.close(); + } + } + } + catch (IOException ioe) + { + //ignore + } + return null; + } + + + public AccessResult isAuthorized(Accessable accessObject, String username) + { + return isAuthorized(accessObject, new UsernamePrincipal(username), AccessRights.Rights.READ); + } + + public AccessResult isAuthorized(Accessable accessObject, Principal user, AccessRights.Rights rights) + { + + if (accessObject instanceof VirtualHost) + { + String[] hosts = lookupVirtualHost(user.getName()); + + if (hosts != null) + { + for (String host : hosts) + { + if (accessObject.getAccessableName().equals(host)) + { + return new AccessResult(this, AccessResult.AccessStatus.GRANTED); + } + } + } + } + + return new AccessResult(this, AccessResult.AccessStatus.REFUSED); + } + + public String getName() + { + return "PlainPasswordVhostFile"; + } + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabase.java new file mode 100644 index 0000000000..a82f9ed40b --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java new file mode 100644 index 0000000000..2c553ae76a --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java new file mode 100644 index 0000000000..73d58ca489 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.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"); + } + char[] pwd = _users.getProperty(principal.getName()).toCharArray(); + 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 + { + //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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java new file mode 100644 index 0000000000..6b86a46bd2 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java new file mode 100644 index 0000000000..bb94e0b7bf --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.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.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); + +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java new file mode 100644 index 0000000000..ce5e0cd748 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.server.security.auth.manager; + +import org.apache.log4j.Logger; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.SubsetConfiguration; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +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; + + 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) + { + + if (hostConfig instanceof SubsetConfiguration) + { + _logger.warn("No authentication specified for '" + ((SubsetConfiguration) hostConfig).getPrefix() + "'. Using Default authentication manager"); + } + else + { + _logger.warn("No authentication specified. Using Default authentication manager"); + } + _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(providerMap), 1) == -1) + { + _logger.warn("Unable to set order of providers."); + } + } + 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); + } + } + + public AuthenticationResult isAuthorize(VirtualHost vhost, String username) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java new file mode 100644 index 0000000000..89e545d6f5 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java new file mode 100644 index 0000000000..fd4ad86055 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.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.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(Map> providerMap) + { + super("AMQSASLProvider", 1.0, "A JCA provider that registers all " + + "AMQ SASL providers that want to be registered"); + register(providerMap); + //Security.addProvider(this); + } + + private void register(Map> providerMap) + { + for (Map.Entry> me : + providerMap.entrySet()) + { + put("SaslServerFactory." + me.getKey(), me.getValue().getName()); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java new file mode 100644 index 0000000000..dd0bd096c3 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java new file mode 100644 index 0000000000..d7c8383690 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainInitialiser.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java new file mode 100644 index 0000000000..7842f376fb --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.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.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"); + passwordCb.setPassword(pwd.toCharArray()); + AuthorizeCallback authzCb = new AuthorizeCallback(username, username); + Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; + _cbh.handle(callbacks); + _complete = true; + if (authzCb.isAuthorized()) + { + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedInitialiser.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedSaslServer.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HashedServerFactory.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5Initialiser.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainInitialiser.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java new file mode 100644 index 0000000000..36aeb77fe1 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.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.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); + // 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... + int passwordLen = response.length - authcidNullPosition - 1; + String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8"); + passwordCb.setPassword(pwd.toCharArray()); + AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid); + Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; + _cbh.handle(callbacks); + _complete = true; + if (authzCb.isAuthorized()) + { + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java b/Final2/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/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQState.java new file mode 100644 index 0000000000..f427cc7206 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java new file mode 100644 index 0000000000..f96900d0a9 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.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. + * + */ +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.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicAckBody; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicGetBody; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.BasicQosBody; +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.BasicRejectBody; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ChannelFlowBody; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.framing.ConnectionOpenBody; +import org.apache.qpid.framing.ConnectionSecureOkBody; +import org.apache.qpid.framing.ConnectionStartOkBody; +import org.apache.qpid.framing.ConnectionTuneOkBody; +import org.apache.qpid.framing.ExchangeBoundBody; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeleteBody; +import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.QueuePurgeBody; +import org.apache.qpid.framing.TxCommitBody; +import org.apache.qpid.framing.TxRollbackBody; +import org.apache.qpid.framing.TxSelectBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; +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) + { + this(AMQState.CONNECTION_NOT_STARTED, true, virtualHostRegistry, protocolSession); + } + + protected AMQStateManager(AMQState initial, boolean register, VirtualHostRegistry virtualHostRegistry, + AMQProtocolSession protocolSession) + { + _virtualHostRegistry = virtualHostRegistry; + _protocolSession = protocolSession; + _currentState = initial; + if (register) + { + registerListeners(); + } + } + + protected void registerListeners() + { + Map, StateAwareMethodListener> frame2handlerMap; + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + frame2handlerMap.put(ConnectionStartOkBody.class, ConnectionStartOkMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + frame2handlerMap.put(ConnectionSecureOkBody.class, ConnectionSecureOkMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_AUTH, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + frame2handlerMap.put(ConnectionTuneOkBody.class, ConnectionTuneOkMethodHandler.getInstance()); + _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>(); + frame2handlerMap.put(ChannelOpenBody.class, ChannelOpenHandler.getInstance()); + frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseHandler.getInstance()); + frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + frame2handlerMap.put(ExchangeDeclareBody.class, ExchangeDeclareHandler.getInstance()); + frame2handlerMap.put(ExchangeDeleteBody.class, ExchangeDeleteHandler.getInstance()); + frame2handlerMap.put(ExchangeBoundBody.class, ExchangeBoundHandler.getInstance()); + frame2handlerMap.put(BasicAckBody.class, BasicAckMethodHandler.getInstance()); + frame2handlerMap.put(BasicRecoverBody.class, BasicRecoverMethodHandler.getInstance()); + frame2handlerMap.put(BasicConsumeBody.class, BasicConsumeMethodHandler.getInstance()); + frame2handlerMap.put(BasicGetBody.class, BasicGetMethodHandler.getInstance()); + frame2handlerMap.put(BasicCancelBody.class, BasicCancelMethodHandler.getInstance()); + frame2handlerMap.put(BasicPublishBody.class, BasicPublishMethodHandler.getInstance()); + frame2handlerMap.put(BasicQosBody.class, BasicQosHandler.getInstance()); + frame2handlerMap.put(QueueBindBody.class, QueueBindHandler.getInstance()); + frame2handlerMap.put(QueueDeclareBody.class, QueueDeclareHandler.getInstance()); + frame2handlerMap.put(QueueDeleteBody.class, QueueDeleteHandler.getInstance()); + frame2handlerMap.put(QueuePurgeBody.class, QueuePurgeHandler.getInstance()); + frame2handlerMap.put(ChannelFlowBody.class, ChannelFlowHandler.getInstance()); + frame2handlerMap.put(TxSelectBody.class, TxSelectHandler.getInstance()); + frame2handlerMap.put(TxCommitBody.class, TxCommitHandler.getInstance()); + frame2handlerMap.put(TxRollbackBody.class, TxRollbackHandler.getInstance()); + frame2handlerMap.put(BasicRejectBody.class, BasicRejectMethodHandler.getInstance()); + + _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); + + frame2handlerMap = new HashMap, StateAwareMethodListener>(); + frame2handlerMap.put(ConnectionCloseOkBody.class, ConnectionCloseOkMethodHandler.getInstance()); + _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 + { + StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod()); + if (handler != null) + { + + checkChannel(evt, _protocolSession); + + handler.methodReceived(this, evt); + + return true; + } + + return false; + } + + 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/Final2/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/IllegalStateTransitionException.java new file mode 100644 index 0000000000..cec67a8a6d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateAwareMethodListener.java new file mode 100644 index 0000000000..e3af0bc486 --- /dev/null +++ b/Final2/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, AMQMethodEvent evt) throws AMQException; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/state/StateListener.java new file mode 100644 index 0000000000..00fc09867b --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java new file mode 100644 index 0000000000..8ccb0be0a8 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.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.store; + +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.AtomicLong; + +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.AMQQueue; +import org.apache.qpid.server.queue.MessageMetaData; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.Exchange; + +/** + * 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); + + 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 + { + if (_metaDataMap != null) + { + _metaDataMap.clear(); + _metaDataMap = null; + } + if (_contentBodyMap != null) + { + _contentBodyMap.clear(); + _contentBodyMap = null; + } + } + + public void removeMessage(StoreContext context, Long messageId) + { + 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 required to do anything + } + + public void removeQueue(AMQShortString name) throws AMQException + { + // Not required to do anything + } + + public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException + { + // Not required to do anything + } + + public void dequeueMessage(StoreContext context, AMQShortString name, 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 + { + 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 + { + _metaDataMap.put(messageId, messageMetaData); + } + + public MessageMetaData getMessageMetaData(StoreContext context,Long messageId) throws AMQException + { + return _metaDataMap.get(messageId); + } + + public ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException + { + List bodyList = _contentBodyMap.get(messageId); + return bodyList.get(index); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java new file mode 100644 index 0000000000..2a83d9b649 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -0,0 +1,261 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.AMQQueue; +import org.apache.qpid.server.queue.MessageMetaData; +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; + + /** + * Removes the specified queue from the persistent store. + * + * @param name The queue to remove. + * + * @throws AMQException If the operation fails for any reason. + */ + void removeQueue(AMQShortString name) throws AMQException; + + /** + * Places a message onto a specified queue, in a given transactional context. + * + * @param context The transactional context for the operation. + * @param name The name of 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, AMQShortString name, 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 name The name of the queue to take the message from. + * @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, AMQShortString name, 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; +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java new file mode 100644 index 0000000000..3ee49d58cf --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.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.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 = super.toString(); + } + + public StoreContext(String name) + { + _name = name; + } + + public Object getPayload() + { + return _payload; + } + + public void setPayload(Object payload) + { + _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/Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.java new file mode 100644 index 0000000000..a4ed859fa7 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ConnectorConfiguration.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.transport; + +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.qpid.configuration.Configured; + +public class ConnectorConfiguration +{ + 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; + + public IoAcceptor createAcceptor() + { + return new org.apache.mina.transport.socket.nio.SocketAcceptor(processors, new NewThreadExecutor()); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/transport/ThreadPoolFilter.java new file mode 100644 index 0000000000..bdd27f2d1c --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java new file mode 100644 index 0000000000..988f589339 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.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.txn; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.NoConsumersException; +import org.apache.qpid.server.store.StoreContext; + +import java.util.List; + +/** + * @author Apache Software Foundation + */ +public class CleanupMessageOperation implements TxnOp +{ + private static final Logger _log = Logger.getLogger(CleanupMessageOperation.class); + + private final AMQMessage _msg; + + private final List _returns; + + public CleanupMessageOperation(AMQMessage msg, List returns) + { + _msg = msg; + _returns = returns; + } + + public void prepare(StoreContext context) throws AMQException + { } + + public void undoPrepare() + { + // don't need to do anything here, if the store's txn failed + // when processing prepare then the message was not stored + // or enqueued on any queues and can be discarded + } + + public void commit(StoreContext context) + { + // No-op can't be done here has this is before the message has been attempted to be delivered. + /*try + { + _msg.checkDeliveredToConsumer(); + } + catch (NoConsumersException e) + { + _returns.add(e); + }*/ + } + + public void rollback(StoreContext context) + { + // NO OP + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java new file mode 100644 index 0000000000..9068f871cb --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java @@ -0,0 +1,262 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.txn; + +import org.apache.log4j.Logger; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.RequiredDeliveryException; +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.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.NoConsumersException; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreContext; + +import java.util.LinkedList; +import java.util.List; + +/** 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 LinkedList(); + + /** + * 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 List _returnMessages; + + private final MessageStore _messageStore; + + private final StoreContext _storeContext; + + private boolean _inTran = false; + + /** Are there messages to deliver. NOT Has the message been delivered */ + private boolean _messageDelivered = false; + + private static class DeliveryDetails + { + public AMQMessage message; + public AMQQueue queue; + private boolean deliverFirst; + + public DeliveryDetails(AMQMessage message, AMQQueue queue, boolean deliverFirst) + { + this.message = message; + this.queue = queue; + this.deliverFirst = deliverFirst; + } + } + + public LocalTransactionalContext(MessageStore messageStore, StoreContext storeContext, + List returnMessages) + { + _messageStore = messageStore; + _storeContext = storeContext; + _returnMessages = returnMessages; + // _txnBuffer.enlist(new StoreMessageOperation(messageStore)); + } + + public StoreContext getStoreContext() + { + return _storeContext; + } + + public void rollback() throws AMQException + { + _txnBuffer.rollback(_storeContext); + // Hack to deal with uncommitted non-transactional writes + if (_messageStore.inTran(_storeContext)) + { + _messageStore.abortTran(_storeContext); + _inTran = false; + } + + _postCommitDeliveryList.clear(); + } + + public void deliver(AMQMessage message, AMQQueue queue, boolean deliverFirst) 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. Finally a cleanup op will be added to decrement + // the reference associated with the routing. + // message.incrementReference(); + _postCommitDeliveryList.add(new DeliveryDetails(message, queue, deliverFirst)); + _messageDelivered = true; + _txnBuffer.enlist(new CleanupMessageOperation(message, _returnMessages)); + /*_txnBuffer.enlist(new DeliverMessageOperation(message, queue)); + if (_log.isDebugEnabled()) + { + _log.debug("Incrementing ref count on message and enlisting cleanup operation - id " + + message.getMessageId()); + } + message.incrementReference(); + _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) + { + beginTranIfNecessary(); + _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); + } + } + + 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); + } + + _messageStore.beginTran(_storeContext); + _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(_messageStore)); + } + // fixme fail commit here ... QPID-440 + try + { + _txnBuffer.commit(_storeContext); + } + finally + { + _messageDelivered = false; + _inTran = _messageStore.inTran(_storeContext); + } + + try + { + postCommitDelivery(_returnMessages); + } + catch (AMQException e) + { + // OK so what do we do now...? + _log.error("Failed to deliver messages following txn commit: " + e, e); + } + } + + private void postCommitDelivery(List returnMessages) throws AMQException + { + if (_log.isDebugEnabled()) + { + _log.debug("Performing post commit delivery"); + } + + try + { + for (DeliveryDetails dd : _postCommitDeliveryList) + { + dd.queue.process(_storeContext, dd.message, dd.deliverFirst); + + try + { + dd.message.checkDeliveredToConsumer(); + } + catch (NoConsumersException nce) + { + returnMessages.add(nce); + } + } + } + finally + { + _postCommitDeliveryList.clear(); + } + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java new file mode 100644 index 0000000000..b3d69543d4 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java @@ -0,0 +1,227 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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 java.util.Set; + +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.UnacknowledgedMessage; +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.AMQQueue; +import org.apache.qpid.server.queue.NoConsumersException; +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 Set _browsedAcks; + + private final MessageStore _messageStore; + + private StoreContext _storeContext; + + /** Whether we are in a transaction */ + private boolean _inTran; + + public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel, + List returnMessages, Set browsedAcks) + { + _channel = channel; + _storeContext = storeContext; + _returnMessages = returnMessages; + _messageStore = messageStore; + _browsedAcks = browsedAcks; + } + + + 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(AMQMessage message, AMQQueue queue, boolean deliverFirst) throws AMQException + { + try + { + queue.process(_storeContext, message, deliverFirst); + //following check implements the functionality + //required by the 'immediate' flag: + message.checkDeliveredToConsumer(); + } + catch (NoConsumersException e) + { + _returnMessages.add(e); + } + } + + public void acknowledgeMessage(final long deliveryTag, long lastDeliveryTag, + boolean multiple, final UnacknowledgedMessageMap unacknowledgedMessageMap) + throws AMQException + { + 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(UnacknowledgedMessage message) throws AMQException + { + if (!_browsedAcks.contains(deliveryTag)) + { + if (_log.isDebugEnabled()) + { + _log.debug("Discarding message: " + message.message.getMessageId()); + } + + //Message has been ack so discard it. This will dequeue and decrement the reference. + message.discard(_storeContext); + } + else + { + _browsedAcks.remove(deliveryTag); + } + 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"); + } + + LinkedList acked = new LinkedList(); + unacknowledgedMessageMap.drainTo(acked, deliveryTag); + for (UnacknowledgedMessage msg : acked) + { + if (!_browsedAcks.contains(deliveryTag)) + { + if (_log.isDebugEnabled()) + { + _log.debug("Discarding message: " + msg.message.getMessageId()); + } + + //Message has been ack so discard it. This will dequeue and decrement the reference. + msg.discard(_storeContext); + } + else + { + _browsedAcks.remove(deliveryTag); + } + } + } + } + else + { + UnacknowledgedMessage msg; + msg = unacknowledgedMessageMap.remove(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 (!_browsedAcks.contains(deliveryTag)) + { + if (_log.isDebugEnabled()) + { + _log.debug("Discarding message: " + msg.message.getMessageId()); + } + + //Message has been ack so discard it. This will dequeue and decrement the reference. + msg.discard(_storeContext); + } + else + { + _browsedAcks.remove(deliveryTag); + } + + if (_log.isDebugEnabled()) + { + _log.debug("Received non-multiple ack for messaging with delivery tag " + deliveryTag + " msg id " + + msg.message.getMessageId()); + } + } + } + + public void messageFullyReceived(boolean persistent) throws AMQException + { + if (persistent) + { + _messageStore.commitTran(_storeContext); + _inTran = false; + } + } + + public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException + { + _channel.processReturns(protocolSession); + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/StoreMessageOperation.java new file mode 100644 index 0000000000..0e4d6c2030 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java new file mode 100644 index 0000000000..fee25c07df --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.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.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.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. A 'deliverFirst' flag may be set if the message is a + * redelivery, and should be placed on the front of the queue. + * + *

This is an 'enqueue' operation. + * + * @param message The message to deliver. + * @param queue The queue to deliver the message to. + * @param deliverFirst true to place the message on the front of the queue for redelivery, false + * for normal FIFO message ordering. + * + * @throws AMQException If the message cannot be delivered for any reason. + */ + void deliver(AMQMessage message, AMQQueue queue, boolean deliverFirst) 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/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnBuffer.java new file mode 100644 index 0000000000..46a68b6a23 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/txn/TxnOp.java new file mode 100644 index 0000000000..919c078cf0 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/util/CircularBuffer.java new file mode 100644 index 0000000000..e730e2f3c3 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/util/ConcurrentLinkedQueueNoSize.java new file mode 100644 index 0000000000..cf5e71a6e2 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/util/LoggingProxy.java new file mode 100644 index 0000000000..eda97e0ed2 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.java new file mode 100644 index 0000000000..150b98b424 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/util/NullApplicationRegistry.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.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.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.AccessManager; +import org.apache.qpid.server.security.access.AllowAll; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +public class NullApplicationRegistry extends ApplicationRegistry +{ + private ManagedObjectRegistry _managedObjectRegistry; + + private AuthenticationManager _authenticationManager; + + private VirtualHostRegistry _virtualHostRegistry; + + private AccessManager _accessManager; + + private PrincipalDatabaseManager _databaseManager; + + + public NullApplicationRegistry() + { + super(new MapConfiguration(new HashMap())); + } + + public void initialise() throws Exception + { + _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"); + + _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes + + } + + public Configuration getConfiguration() + { + return _configuration; + } + + + public ManagedObjectRegistry getManagedObjectRegistry() + { + return _managedObjectRegistry; + } + + public PrincipalDatabaseManager getDatabaseManager() + { + return _databaseManager; + } + + public AuthenticationManager getAuthenticationManager() + { + return _authenticationManager; + } + + public Collection getVirtualHostNames() + { + String[] hosts = {"test"}; + return Arrays.asList(hosts); + } + + public VirtualHostRegistry getVirtualHostRegistry() + { + return _virtualHostRegistry; + } + + public AccessManager getAccessManager() + { + return _accessManager; + } +} + + diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/ManagedVirtualHost.java new file mode 100644 index 0000000000..85d804457e --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java new file mode 100644 index 0000000000..b95772b680 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.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.server.virtualhost; + +import javax.management.NotCompliantMBeanException; + +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.server.AMQBrokerManagerMBean; +import org.apache.qpid.server.security.access.AccessManager; +import org.apache.qpid.server.security.access.AccessManagerImpl; +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.registry.ApplicationRegistry; +import org.apache.qpid.server.store.MessageStore; + +public class VirtualHost implements Accessable +{ + private static final Logger _logger = Logger.getLogger(VirtualHost.class); + + + private final String _name; + + private QueueRegistry _queueRegistry; + + private ExchangeRegistry _exchangeRegistry; + + private ExchangeFactory _exchangeFactory; + + private MessageStore _messageStore; + + protected VirtualHostMBean _virtualHostMBean; + + private AMQBrokerManagerMBean _brokerMBean; + + private AuthenticationManager _authenticationManager; + + private AccessManager _accessManager; + + + public void setAccessableName(String name) + { + _logger.warn("Setting Accessable Name for VirualHost is not allowed. (" + + name + ") ignored remains :" + getAccessableName()); + } + + public String getAccessableName() + { + return _name; + } + + + /** + * 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, null, store); + } + + /** + * Normal Constructor + * @param name + * @param hostConfig + * @throws Exception + */ + public VirtualHost(String name, Configuration hostConfig) throws Exception + { + this(name, hostConfig, null); + } + + private VirtualHost(String name, Configuration hostConfig, MessageStore store) throws Exception + { + _name = name; + + _virtualHostMBean = new VirtualHostMBean(); + // This isn't needed to be registered + //_virtualHostMBean.register(); + + _queueRegistry = new DefaultQueueRegistry(this); + _exchangeFactory = new DefaultExchangeFactory(this); + _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 = new AccessManagerImpl(name, hostConfig); + + _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); + _brokerMBean.register(); + } + + 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 AccessManager getAccessManager() + { + return _accessManager; + } + + public void close() throws Exception + { + if (_messageStore != null) + { + _messageStore.close(); + } + } + + public ManagedObject getBrokerMBean() + { + return _brokerMBean; + } + + public ManagedObject getManagedObject() + { + return _virtualHostMBean; + } +} diff --git a/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java b/Final2/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostRegistry.java new file mode 100644 index 0000000000..27917fac8a --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java new file mode 100644 index 0000000000..edc900f401 --- /dev/null +++ b/Final2/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.queue.AMQQueue; +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.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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/AbstractCommand.java new file mode 100644 index 0000000000..5444197cb4 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Clear.java new file mode 100644 index 0000000000..b0006b3fe6 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Command.java new file mode 100644 index 0000000000..bfa775a34a --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java new file mode 100644 index 0000000000..a5b3a87616 --- /dev/null +++ b/Final2/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.server.queue.AMQQueue; +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java new file mode 100644 index 0000000000..eea53252c6 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java @@ -0,0 +1,299 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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 (AMQMessage msg : messages) + { + 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(msg); + + 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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Help.java new file mode 100644 index 0000000000..0f9546541b --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java new file mode 100644 index 0000000000..df8b59ec19 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Load.java new file mode 100644 index 0000000000..244a311c30 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java new file mode 100644 index 0000000000..25cff27445 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.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.tools.messagestore.commands; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.AMQQueue; +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 (AMQMessage msg : messages) + { + ids.add(msg.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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java new file mode 100644 index 0000000000..f187e26593 --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.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.tools.messagestore.commands; + +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.tools.messagestore.MessageStoreTool; + +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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Quit.java new file mode 100644 index 0000000000..a81bc07c38 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java new file mode 100644 index 0000000000..fd7d4c3f13 --- /dev/null +++ b/Final2/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.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +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/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java new file mode 100644 index 0000000000..5988cdabfc --- /dev/null +++ b/Final2/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.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.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.AMQQueue; +import org.apache.qpid.tools.messagestore.MessageStoreTool; +import org.apache.qpid.tools.utils.Console; + +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; + +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 (AMQMessage msg : messages) + { + 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/Final2/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/security/Passwd.java new file mode 100644 index 0000000000..c27c52eb8e --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/CommandParser.java new file mode 100644 index 0000000000..986fea32cc --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/Console.java new file mode 100644 index 0000000000..cf457d1ea5 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleCommandParser.java new file mode 100644 index 0000000000..09444ccdd7 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java b/Final2/java/broker/src/main/java/org/apache/qpid/tools/utils/SimpleConsole.java new file mode 100644 index 0000000000..ec080a4611 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java new file mode 100644 index 0000000000..b8803206e0 --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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 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.out.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.out.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.out.println("Waiting for process to exit: " + args[0]); + task.waitFor(); + System.out.println("Done Proccess: " + args[0]); + + } + catch (IOException e) + { + System.out.println("Proccess had problems: " + e.getMessage()); + exit(1); + } + catch (InterruptedException e) + { + System.out.println("Proccess had problems: " + e.getMessage()); + + 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/Final2/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/configuration/TestPropertyUtils.java new file mode 100644 index 0000000000..3b83190e42 --- /dev/null +++ b/Final2/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/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java new file mode 100644 index 0000000000..0a3bc93763 --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java @@ -0,0 +1,607 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.AMQQueue; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.MessageHandleFactory; +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.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +import java.util.HashSet; +import java.util.List; +import java.util.LinkedList; + +public class DestWildExchangeTest extends TestCase +{ + + DestWildExchange _exchange; + + VirtualHost _vhost; + MessageStore _store; + StoreContext _context; + + + public void setUp() throws AMQException + { + _exchange = new DestWildExchange(); + _vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + _store = new MemoryMessageStore(); + _context = new StoreContext(); + } + + + public void testNoRoute() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a*#b"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + MessagePublishInfo info = new PublishInfo(new AMQShortString("a.b")); + + AMQMessage message = new AMQMessage(0L, info, null); + + try + { + _exchange.route(message); + fail("Message has no route and shouldn't be routed"); + } + catch (NoRouteException nre) + { + //normal + } + + Assert.assertEquals(0, queue.getMessageCount()); + } + + public void testDirectMatch() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("ab"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + AMQMessage message = createMessage("a.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 = new AMQQueue(new AMQShortString("a*"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.*"), queue, null); + + + AMQMessage message = createMessage("a.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.#"), queue, null); + + + AMQMessage message = createMessage("a.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null); + + + AMQMessage message = createMessage("a.c.d.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.c.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMatchafterHash() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null); + + + AMQMessage message = createMessage("a.c.b.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + + message = createMessage("a.a.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + message = createMessage("a.b.c.b"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + + public void testHashAfterHash() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null); + + + AMQMessage message = createMessage("a.c.b.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testHashHash() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null); + + + AMQMessage message = createMessage("a.c.b.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + 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 + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + } + catch (AMQException nre) + { + fail("Message has no route and should be routed"); + } + + Assert.assertEquals(1, queue.getMessageCount()); + + Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0)); + + queue.deleteMessageFromTop(_context); + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testSubMatchFails() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null); + + + AMQMessage message = createMessage("a.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMoreRouting() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + AMQMessage message = createMessage("a.b.c"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + public void testMoreQueue() throws AMQException + { + AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost); + _exchange.registerQueue(new AMQShortString("a.b"), queue, null); + + + AMQMessage message = createMessage("a"); + + try + { + _exchange.route(message); + message.routingComplete(_store, _context, new MessageHandleFactory()); + fail("Message has route and should not be routed"); + } + catch (AMQException nre) + { + } + + Assert.assertEquals(0, queue.getMessageCount()); + + } + + private AMQMessage createMessage(String s) throws AMQException + { + MessagePublishInfo info = new PublishInfo(new AMQShortString(s)); + + TransactionalContext trancontext = new NonTransactionalContext(_store, _context, null, + new LinkedList(), + new HashSet()); + + AMQMessage message = new AMQMessage(0L, info, trancontext); + message.setContentHeaderBody(new ContentHeaderBody()); + + return message; + } + + + class PublishInfo implements MessagePublishInfo + { + AMQShortString _routingkey; + + PublishInfo(AMQShortString routingkey) + { + _routingkey = routingkey; + } + + public AMQShortString getExchange() + { + return null; + } + + public boolean isImmediate() + { + return false; + } + + public boolean isMandatory() + { + return true; + } + + public AMQShortString getRoutingKey() + { + return _routingkey; + } + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java new file mode 100644 index 0000000000..18d8592817 --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.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.exchange; + +import junit.framework.TestCase; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.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.CompositeData; +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 + { + DestNameExchange exchange = new DestNameExchange(); + 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 + { + DestWildExchange exchange = new DestWildExchange(); + 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(); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _queueRegistry = _virtualHost.getQueueRegistry(); + _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost); + _queueRegistry.registerQueue(_queue); + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/exchange/HeadersBindingTest.java new file mode 100644 index 0000000000..86ba96bf5d --- /dev/null +++ b/Final2/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/Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestIoSession.java new file mode 100644 index 0000000000..ff4d3ed9fb --- /dev/null +++ b/Final2/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/Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java new file mode 100644 index 0000000000..0c0d8f471e --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.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.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.output.ProtocolOutputConverter; +import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; + +public class TestMinaProtocolSession extends AMQMinaProtocolSession +{ + public TestMinaProtocolSession() throws AMQException + { + super(new TestIoSession(), + ApplicationRegistry.getInstance().getVirtualHostRegistry(), + new AMQCodecFactory(true)); + } + + public ProtocolOutputConverter getProtocolOutputConverter() + { + return ProtocolOutputConverterRegistry.getConverter(this); + } + + public byte getProtocolMajorVersion() + { + return (byte)8; + } + + public byte getProtocolMinorVersion() + { + return (byte)0; + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java new file mode 100644 index 0000000000..b718b9c861 --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -0,0 +1,301 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * 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.protocol.TestMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; + +import javax.management.Notification; +import java.util.LinkedList; +import java.util.HashSet; + +/** 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 = null; + private MessageStore _messageStore = new MemoryMessageStore(); + private StoreContext _storeContext = new StoreContext(); + private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList(), + new HashSet()); + + /** + * Tests if the alert gets thrown when message count increases the threshold limit + * + * @throws Exception + */ + public void testMessageCountAlert() throws Exception + { + _queue = new AMQQueue(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost); + _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 = new AMQQueue(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost); + _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 = new AMQQueue(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost); + _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 = new AMQQueue(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"), + false, _virtualHost); + _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. + The QueueDepth should decrease when messages are delivered from the queue (QPID-408) + */ + public void testQueueDepthAlertWithSubscribers() throws Exception + { + protocolSession = new TestMinaProtocolSession(); + AMQChannel channel = new AMQChannel(protocolSession, 2, _messageStore, null); + protocolSession.addChannel(channel); + + // Create queue + _queue = getNewQueue(); + _queue.registerProtocolSession(protocolSession, channel.getChannelId(), + new AMQShortString("consumer_tag"), true, null, false, 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(0), 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.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag")); + 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. + _queue.registerProtocolSession(protocolSession, channel.getChannelId(), + new AMQShortString("consumer_tag"), true, null, false, false); + _queue.deliverAsync(); + while (_queue.getMessageCount() != 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.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag")); + 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())); + } + + protected AMQMessage message(final boolean immediate, long size) throws AMQException + { + MessagePublishInfo publish = new MessagePublishInfo() + { + + public AMQShortString getExchange() + { + return null; + } + + public boolean isImmediate() + { + return immediate; + } + + public boolean isMandatory() + { + return false; + } + + public AMQShortString getRoutingKey() + { + return null; + } + }; + + ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); + contentHeaderBody.bodySize = size; // in bytes + AMQMessage message = new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext); + message.setContentHeaderBody(contentHeaderBody); + message.setPublisher(protocolSession); + return message; + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + } + + private void sendMessages(long messageCount, long size) throws AMQException + { + AMQMessage[] messages = new AMQMessage[(int) messageCount]; + for (int i = 0; i < messages.length; i++) + { + messages[i] = message(false, size); + messages[i].enqueue(_queue); + messages[i].routingComplete(_messageStore, _storeContext, new MessageHandleFactory()); + } + + for (int i = 0; i < messageCount; i++) + { + _queue.process(_storeContext, messages[i], false); + } + } + + private AMQQueue getNewQueue() throws AMQException + { + return new AMQQueue(new AMQShortString("testQueue" + Math.random()), + false, + new AMQShortString("AMQueueAlertTest"), + false, + _virtualHost); + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java new file mode 100644 index 0000000000..3caf6ad73d --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.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.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.server.AMQChannel; +import org.apache.qpid.server.RequiredDeliveryException; +import org.apache.qpid.server.protocol.TestMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolSession; +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.LinkedList; +import java.util.HashSet; + +/** + * 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; + + 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(); + assertTrue(_queueMBean.getMessageCount() == 0); + 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 + { + SubscriptionManager mgr = _queue.getSubscribers(); + assertFalse(mgr.hasActiveSubscribers()); + assertTrue(_queueMBean.getActiveConsumerCount() == 0); + + + TestMinaProtocolSession protocolSession = new TestMinaProtocolSession(); + AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore, null); + protocolSession.addChannel(channel); + + _queue.registerProtocolSession(protocolSession, 1, new AMQShortString("test"), false, null, false, false); + assertTrue(_queueMBean.getActiveConsumerCount() == 1); + + SubscriptionSet _subscribers = (SubscriptionSet) mgr; + SubscriptionFactory subscriptionFactory = new SubscriptionImpl.Factory(); + Subscription s1 = subscriptionFactory.createSubscription(channel.getChannelId(), + protocolSession, + new AMQShortString("S1"), + false, + null, + true, + _queue); + + Subscription s2 = subscriptionFactory.createSubscription(channel.getChannelId(), + protocolSession, + new AMQShortString("S2"), + false, + null, + true, + _queue); + _subscribers.addSubscriber(s1); + _subscribers.addSubscriber(s2); + assertTrue(_queueMBean.getActiveConsumerCount() == 3); + assertTrue(_queueMBean.getConsumerCount() == 3); + + s1.close(); + assertTrue(_queueMBean.getActiveConsumerCount() == 2); + 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) + { + + } + + AMQMessage msg = message(false, false); + long id = msg.getMessageId(); + _queue.clearQueue(_storeContext); + + msg.enqueue(_queue); + msg.routingComplete(_messageStore, _storeContext, new MessageHandleFactory()); + _queue.process(_storeContext, msg, false); + _queueMBean.viewMessageContent(id); + try + { + _queueMBean.viewMessageContent(id + 1); + fail(); + } + catch (JMException ex) + { + + } + } + + private AMQMessage message(final boolean immediate, boolean persistent) throws AMQException + { + MessagePublishInfo publish = new MessagePublishInfo() + { + + public AMQShortString getExchange() + { + return null; + } + + 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)); + return new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, contentHeaderBody); + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance(); + _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test"); + _messageStore = _virtualHost.getMessageStore(); + + _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext, + null, + new LinkedList(), + new HashSet()); + + _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost); + _queueMBean = new AMQQueueMBean(_queue); + + _protocolSession = new TestMinaProtocolSession(); + } + + private void sendMessages(int messageCount, boolean persistent) throws AMQException + { + for (int i = 0; i < messageCount; i++) + { + AMQMessage currentMessage = message(false, persistent); + currentMessage.enqueue(_queue); + + // route header + currentMessage.routingComplete(_messageStore, _storeContext, new MessageHandleFactory()); + + // Add the body so we have somthing to test later + currentMessage.addContentBodyFrame(_storeContext, + _protocolSession.getRegistry() + .getProtocolVersionMethodConverter() + .convertToContentChunk( + new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE), + MESSAGE_SIZE))); + + + } + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java new file mode 100644 index 0000000000..48d808142c --- /dev/null +++ b/Final2/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +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.List; + +/** + * Adds some extra methods to the memory message store for testing purposes. + */ +public class TestableMemoryMessageStore extends MemoryMessageStore +{ + + MemoryMessageStore _mms = null; + + 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; + } + } +} diff --git a/Final2/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java b/Final2/java/broker/src/test/java/org/apache/qpid/server/util/LoggingProxyTest.java new file mode 100644 index 0000000000..c7db51016e --- /dev/null +++ b/Final2/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/Final2/java/client-java14/README.txt b/Final2/java/client-java14/README.txt new file mode 100644 index 0000000000..66621c7eb2 --- /dev/null +++ b/Final2/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/Final2/java/client-java14/etc/sasl.properties b/Final2/java/client-java14/etc/sasl.properties new file mode 100644 index 0000000000..04519e2a30 --- /dev/null +++ b/Final2/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/Final2/java/client-java14/pom.xml b/Final2/java/client-java14/pom.xml new file mode 100644 index 0000000000..a2da7800e6 --- /dev/null +++ b/Final2/java/client-java14/pom.xml @@ -0,0 +1,224 @@ + + + + 4.0.0 + org.apache.qpid + qpid-client-java14 + jar + 1.0-incubating-M2 + Qpid Client for Java 1.4 + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + ../pom.xml + + + + .. + 1.4 + ${pom.version} + ${project.build.directory} + + ${basedir}/etc/sasl.properties + + + path/to/java1.4 + + + + + + + log4j + log4j + + + + org.slf4j + slf4j-simple + + + + org.apache.mina + mina-core + + + + org.apache.geronimo.specs + geronimo-jms_1.1_spec + + + + commons-collections + commons-collections + + + + commons-lang + commons-lang + + + + + org.apache.qpid + qpid-client + jar + ${pom.version} + java14 + + + + + org.apache.qpid + qpid-common + jar + ${pom.version} + java14 + + + + + org.apache.qpid + qpid-integrationtests + jar + ${pom.version} + java14 + + + + + net.sf.retrotranslator + retrotranslator-runtime + package + + + + + junit + junit + test + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.source.version} + ${java.source.version} + + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${assembly.version} + + + + + + assembly-dist + package + + attached + + + + src/main/assembly/client-java14-bin.xml + + qpid-${pom.version} + ${qpid.targetDir} + gnu + + + + + + + assembly-alltestdeps + package + + attached + + + + src/main/assembly/jar-with-dependencies.xml + + target + target/assembly/work + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + surefire-it + integration-test + + test + + + true + once + ${jvm.1.4.bin} + + + amqj.logging.level + ${amqj.logging.level} + + + log4j.configuration + ${log4j.configuration} + + + amq.dynamicsaslregistrar.properties + ${sasl.properties} + + + + + + + + + + diff --git a/Final2/java/client-java14/src/main/assembly/client-java14-bin.xml b/Final2/java/client-java14/src/main/assembly/client-java14-bin.xml new file mode 100644 index 0000000000..91e1f23975 --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/main/assembly/jar-with-dependencies.xml b/Final2/java/client-java14/src/main/assembly/jar-with-dependencies.xml new file mode 100644 index 0000000000..091fd46427 --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java b/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/ClientFactoryImpl.java new file mode 100644 index 0000000000..691020307a --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java b/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/CramMD5Client.java new file mode 100644 index 0000000000..7d15f03329 --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java b/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/PlainClient.java new file mode 100644 index 0000000000..97e607c57e --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java b/Final2/java/client-java14/src/main/java/org/apache/qpid/sasl/Provider.java new file mode 100644 index 0000000000..f9a5c42c3d --- /dev/null +++ b/Final2/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/Final2/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java b/Final2/java/client-java14/src/test/java/org/apache/qpid/test/integration/client/ConnectionTest.java new file mode 100644 index 0000000000..468beda5b5 --- /dev/null +++ b/Final2/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/Final2/java/client/distribution/pom.xml b/Final2/java/client/distribution/pom.xml new file mode 100644 index 0000000000..bd97463d15 --- /dev/null +++ b/Final2/java/client/distribution/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + org.apache.qpid + qpid-client-distribution + jar + 1.0-incubating-M2 + Qpid Client Distributions + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + + + + .. + 1.5 + ${pom.version} + ${project.build.directory} + ${basedir}/.. + + + + + org.apache.qpid + qpid-client + jar + ${pom.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.source.version} + ${java.source.version} + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${assembly.version} + + + src/main/assembly/client-bin.xml + + qpid-${pom.version} + ${qpid.targetDir} + gnu + + + + + org.apache.maven.plugins + maven-jar-plugin + + qpid-incubating + + + true + + + + + + + + + + + maven-assembly-plugin + + + distribution-package + package + + single + + + + src/main/assembly/client-bin.xml + src/main/assembly/client-src.xml + + qpid-${pom.version} + + + + + + + + + + + tests + + + + org.apache.qpid + qpid-client + test-jar + ${project.version} + + + + + + + maven-assembly-plugin + + + distribution-package + package + + single + + + + src/main/assembly/client-bin-tests.xml + + + + + + + + + + + diff --git a/Final2/java/client/distribution/src/main/assembly/client-bin-tests.xml b/Final2/java/client/distribution/src/main/assembly/client-bin-tests.xml new file mode 100644 index 0000000000..ec4df1c9a7 --- /dev/null +++ b/Final2/java/client/distribution/src/main/assembly/client-bin-tests.xml @@ -0,0 +1,107 @@ + + + + java-client-bin-with-tests + false + + tar.gz + zip + + + + + + ../../resources + qpid-${qpid.version} + + DISCLAIMER + LICENSE.txt + NOTICE.txt + README.txt + + + + + ../../release-docs + qpid-${qpid.version}/docs + + RELEASE_NOTES.txt + + + + + + ../src/test + qpid-${qpid.version}/src + + **/*.java + + + + + + + + ../test/etc + qpid-${qpid.version}/etc + + **/* + + + + + + ../test/bin + qpid-${qpid.version}/bin + + **/* + + 777 + + + + + target + qpid-${qpid.version}/lib + + qpid-incubating.jar + + + + + + + qpid-${qpid.version}/lib + false + + org.apache.qpid:qpid-client-distribution + + + + diff --git a/Final2/java/client/distribution/src/main/assembly/client-bin.xml b/Final2/java/client/distribution/src/main/assembly/client-bin.xml new file mode 100644 index 0000000000..70874b09a4 --- /dev/null +++ b/Final2/java/client/distribution/src/main/assembly/client-bin.xml @@ -0,0 +1,78 @@ + + + + java-client-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 + + + + + target + qpid-${qpid.version}/lib + + qpid-incubating.jar + + + + + + + qpid-${qpid.version}/lib + false + + org.apache.qpid:qpid-client:jar:java14 + org.apache.qpid:qpid-common:jar:java14 + org.apache.qpid:qpid-client-distribution + + + + diff --git a/Final2/java/client/distribution/src/main/assembly/client-java1.4-bin.xml b/Final2/java/client/distribution/src/main/assembly/client-java1.4-bin.xml new file mode 100644 index 0000000000..6a2dd17c5c --- /dev/null +++ b/Final2/java/client/distribution/src/main/assembly/client-java1.4-bin.xml @@ -0,0 +1,74 @@ + + + + + java-client-java1.4-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 + + + + + target + qpid-${qpid.version}/lib + + qpid-incubating.jar + + + + + + + qpid-${qpid.version}/lib + false + + org.apache.qpid:qpid-client:jar + org.apache.qpid:qpid-common:jar + org.apache.qpid:qpid-client-distribution + org.apache.mina:mina-java5 + org.apache.mina:mina-filter-ssl + + + + + + diff --git a/Final2/java/client/distribution/src/main/assembly/client-src.xml b/Final2/java/client/distribution/src/main/assembly/client-src.xml new file mode 100644 index 0000000000..b5055f05d7 --- /dev/null +++ b/Final2/java/client/distribution/src/main/assembly/client-src.xml @@ -0,0 +1,62 @@ + + + + + java-client-src + false + + tar.gz + zip + + + + + + + ../../resources + qpid-${qpid.version}-src + + DISCLAIMER + LICENSE.txt + NOTICE.txt + README.txt + + + + + .. + qpid-${qpid.version}-src + + src/main/** + src/test/** + test/main/** + test/test/** + pom.xml + distribution/** + + + **/target + **/target/**/* + **/build + **/build/**/* + + + + diff --git a/Final2/java/client/example/bin/set_classpath.bat b/Final2/java/client/example/bin/set_classpath.bat new file mode 100644 index 0000000000..d528967024 --- /dev/null +++ b/Final2/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/Final2/java/client/example/bin/set_classpath.sh b/Final2/java/client/example/bin/set_classpath.sh new file mode 100755 index 0000000000..89e9bc8242 --- /dev/null +++ b/Final2/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/Final2/java/client/example/pom.xml b/Final2/java/client/example/pom.xml new file mode 100644 index 0000000000..3d4cf21a2d --- /dev/null +++ b/Final2/java/client/example/pom.xml @@ -0,0 +1,152 @@ + + + 4.0.0 + org.apache.qpid + qpid-example + jar + 1.0-incubating-M2 + Qpid Example + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + ../pom.xml + + + + ../.. + warn + + + + + org.apache.qpid + qpid-common + + + + org.apache.qpid + qpid-client + + + + log4j + log4j + + + + org.apache.geronimo.specs + geronimo-jms_1.1_spec + + + + jmscts + jmscts + 0.5-b2 + test + + + jms + jms + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + amqj.noAutoCreateVMBroker + true + + + amqj.logging.level + ${amqj.logging.level} + + + log4j.configuration + file:///${basedir}/src/main/java/log4j.properties + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-SNAPSHOT + + + source-jar.xml + + target + target/assembly/work + + + + attach-artifacts + package + + attached + + + + + + + + + + + diff --git a/Final2/java/client/example/source-jar.xml b/Final2/java/client/example/source-jar.xml new file mode 100644 index 0000000000..60451448b8 --- /dev/null +++ b/Final2/java/client/example/source-jar.xml @@ -0,0 +1,35 @@ + + + + source + + jar + + false + + + src/main/java + + + + diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml b/Final2/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml new file mode 100644 index 0000000000..de64423a51 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/log4j.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java new file mode 100644 index 0000000000..6a7626c51d --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageDispatcher.java @@ -0,0 +1,162 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.publisher; + +import java.io.File; + +import javax.jms.JMSException; + +import org.apache.log4j.Logger; + +import org.apache.qpid.example.shared.FileUtils; +import org.apache.qpid.example.shared.Statics; + +/** + * 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 = Logger.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/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.java new file mode 100644 index 0000000000..f3b21e3c64 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/FileMessageFactory.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.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()); + mfe.initCause(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/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.java new file mode 100644 index 0000000000..0a4231c977 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MessageFactoryException.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.example.publisher; + +public class MessageFactoryException extends Exception +{ + + private int _errorCode; + + public MessageFactoryException(String message) + { + super(message); + } + + public MessageFactoryException(String msg, Throwable t) + { + super(msg, t); + } + + public MessageFactoryException(int errorCode, String msg, Throwable t) + { + super(msg + " [error code " + errorCode + ']', t); + _errorCode = errorCode; + } + + public MessageFactoryException(int errorCode, String msg) + { + super(msg + " [error code " + errorCode + ']'); + _errorCode = errorCode; + } + + public int getErrorCode() + { + return _errorCode; + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorMessageDispatcher.java new file mode 100644 index 0000000000..b6544db995 --- /dev/null +++ b/Final2/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.apache.log4j.BasicConfigurator; +import org.apache.log4j.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 = Logger.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 + * + * @param args + */ + public static void main(String[] args) + { + //Switch on logging appropriately for your app + BasicConfigurator.configure(); + + 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/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.java new file mode 100644 index 0000000000..a67b602e58 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/MonitorPublisher.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.example.publisher; + +import org.apache.log4j.Logger; +import org.apache.qpid.client.BasicMessageProducer; + +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 = Logger.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(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(e); + e.printStackTrace(); + throw new UndeliveredMessageException("Cannot deliver immediate message", e); + } + + _log.info(_name + " finished sending message: " + message); + return true; + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/Publisher.java new file mode 100644 index 0000000000..2bde4ec35c --- /dev/null +++ b/Final2/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.log4j.Logger; + +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; + +public class Publisher +{ + private static final Logger _log = Logger.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(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(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/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.java new file mode 100644 index 0000000000..399cbc9427 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/publisher/UndeliveredMessageException.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.publisher; + +/** + * Exception thrown by monitor when cannot send a message marked for immediate delivery + */ +public class UndeliveredMessageException extends Exception +{ + + private int _errorCode; + + public UndeliveredMessageException(String message) + { + super(message); + } + + public UndeliveredMessageException(String msg, Throwable t) + { + super(msg, t); + } + + public UndeliveredMessageException(int errorCode, String msg, Throwable t) + { + super(msg + " [error code " + errorCode + ']', t); + _errorCode = errorCode; + } + + public UndeliveredMessageException(int errorCode, String msg) + { + super(msg + " [error code " + errorCode + ']'); + _errorCode = errorCode; + } + + public int getErrorCode() + { + return _errorCode; + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Client.java new file mode 100644 index 0000000000..e32ee0ba73 --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java new file mode 100644 index 0000000000..c4edd9034f --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java new file mode 100644 index 0000000000..dd936e429f --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Subscriber.java new file mode 100644 index 0000000000..f2d736701f --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.java new file mode 100644 index 0000000000..6eb847ea9d --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ConnectionException.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.example.shared; + +public class ConnectionException extends Exception +{ + + private int _errorCode; + + public ConnectionException(String message) + { + super(message); + } + + public ConnectionException(String msg, Throwable t) + { + super(msg, t); + } + + public ConnectionException(int errorCode, String msg, Throwable t) + { + super(msg + " [error code " + errorCode + ']', t); + _errorCode = errorCode; + } + + public ConnectionException(int errorCode, String msg) + { + super(msg + " [error code " + errorCode + ']'); + _errorCode = errorCode; + } + + public int getErrorCode() + { + return _errorCode; + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.java new file mode 100644 index 0000000000..bf805ab817 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/ContextException.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.example.shared; + +public class ContextException extends Exception +{ + + private int _errorCode; + + public ContextException(String message) + { + super(message); + } + + public ContextException(String msg, Throwable t) + { + super(msg, t); + } + + public ContextException(int errorCode, String msg, Throwable t) + { + super(msg + " [error code " + errorCode + ']', t); + _errorCode = errorCode; + } + + public ContextException(int errorCode, String msg) + { + super(msg + " [error code " + errorCode + ']'); + _errorCode = errorCode; + } + + public int getErrorCode() + { + return _errorCode; + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/FileUtils.java new file mode 100644 index 0000000000..54446cb6a7 --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java new file mode 100644 index 0000000000..98a2c0d497 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/InitialContextHelper.java @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.example.shared; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.log4j.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 = Logger.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/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/Statics.java new file mode 100644 index 0000000000..c056f8a7da --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties b/Final2/java/client/example/src/main/java/org/apache/qpid/example/shared/example.properties new file mode 100644 index 0000000000..a60e3964ad --- /dev/null +++ b/Final2/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/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriber.java new file mode 100644 index 0000000000..1d2e5e0e66 --- /dev/null +++ b/Final2/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.log4j.Logger; +import org.apache.qpid.example.shared.Statics; + +import javax.jms.*; + +/** + * Subclass of Subscriber which consumes a heartbeat message + */ + +public class MonitoredSubscriber extends Subscriber +{ + protected String _monitorDestinationName; + + private static final Logger _logger = Logger.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/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.java new file mode 100644 index 0000000000..d2f27da052 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/MonitoredSubscriptionWrapper.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.example.subscriber; + +import org.apache.log4j.BasicConfigurator; + +/** + * 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[]) + { + //switch on logging + BasicConfigurator.configure(); + + _subscriber = new MonitoredSubscriber(); + + _subscriber.subscribe(); + } + + /** + * Stop subscribing now ... + */ + public static void stop() + { + _subscriber.stop(); + } +} diff --git a/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.java new file mode 100644 index 0000000000..d443dca828 --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/Subscriber.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.subscriber; + +import org.apache.log4j.Logger; +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; + +/** + * Subscriber which consumes messages from a queue + */ + +public class Subscriber +{ + private static final Logger _log = Logger.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(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/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.java new file mode 100644 index 0000000000..32a0ef685c --- /dev/null +++ b/Final2/java/client/example/src/main/java/org/apache/qpid/example/subscriber/SubscriptionWrapper.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.example.subscriber; + +import org.apache.log4j.BasicConfigurator; + +/** + * 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[]) + { + //switch on logging + BasicConfigurator.configure(); + + _subscriber = new Subscriber(); + + _subscriber.subscribe(); + } + + /** + * Stop subscribing now ... + */ + public static void stop() + { + _subscriber.stop(); + } +} diff --git a/Final2/java/client/pom.xml b/Final2/java/client/pom.xml new file mode 100644 index 0000000000..0f0757e9a3 --- /dev/null +++ b/Final2/java/client/pom.xml @@ -0,0 +1,249 @@ + + + 4.0.0 + org.apache.qpid + qpid-client + jar + 1.0-incubating-M2 + Qpid Client + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + ../pom.xml + + + + .. + 1.5 + ${pom.version} + ${project.build.directory} + + + + + + org.apache.qpid + qpid-common + + + + org.slf4j + slf4j-api + 1.4.0 + + + + org.apache.geronimo.specs + geronimo-jms_1.1_spec + + + + commons-collections + commons-collections + + + + commons-lang + commons-lang + + + + + + + org.apache.qpid + qpid-broker + test + + + + uk.co.thebadgerset + junit-toolkit + test + + + + + net.sf.retrotranslator + retrotranslator-runtime + provided + + + + junit + junit + provided + + + + + + + + + minijar-maven-plugin + org.codehaus.mojo + + + package + + ueberjar + + + false + [artifactId]-[version]-single.jar + single + true + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + target/${artifactId}-${version}-single.jar + jar + single + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + amqj.logging.level + ${amqj.logging.level} + + + log4j.configuration + ${log4j.configuration} + + + amqj.noAutoCreateVMBroker + true + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + package + + test-jar + + + + + + + + org.codehaus.mojo + retrotranslator-maven-plugin + + + retro-client + + translate-project + + + ${project.build.directory}/${project.build.finalName}-java14.jar + ${retrotranslator.verify} + + ${retrotranslator.1.4-rt-path} + ${retrotranslator.1.4-jce-path} + ${retrotranslator.1.4-jsse-path} + ${retrotranslator.1.4-sasl-path} + + false + java14 + true + + + + + + + + + + META-INF/ + false + ../resources/META-INF + + ** + + + + + + + + false + src/main/java + + client.log4j + + + + + + + diff --git a/Final2/java/client/src/main/java/client.log4j b/Final2/java/client/src/main/java/client.log4j new file mode 100644 index 0000000000..525433e9a9 --- /dev/null +++ b/Final2/java/client/src/main/java/client.log4j @@ -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. +# +log4j.rootLogger=${root.logging.level} + + +log4j.logger.org.apache.qpid=${amqj.logging.level}, console +log4j.additivity.org.apache.qpid=false + +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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.java new file mode 100644 index 0000000000..b6fbb6c6bf --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQAuthenticationException.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; + +/** + * 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) + { + super(error, msg); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java new file mode 100644 index 0000000000..c04380ba8c --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java @@ -0,0 +1,353 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.client; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; + +import org.apache.qpid.jms.BrokerDetails; +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 HashMap _options; + + private SSLConfiguration _sslConfiguration; + + public AMQBrokerDetails() + { + _options = new HashMap(); + } + + public AMQBrokerDetails(String url) throws URLSyntaxException + { + this(); + // 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("vm") || + transport.equalsIgnoreCase("tcp")))) + { + 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 + { + //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; + //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 + { + 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 getOption(String key) + { + return _options.get(key); + } + + public void setOption(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 void setTimeout(long timeout) + { + setOption(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); + } + + 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; + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java new file mode 100644 index 0000000000..9abc94b3df --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -0,0 +1,1289 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQUndeliveredException; +import org.apache.qpid.AMQUnresolvedAddressException; +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.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +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.TxSelectBody; +import org.apache.qpid.framing.TxSelectOkBody; +import org.apache.qpid.jms.BrokerDetails; +import org.apache.qpid.jms.ChannelLimitReachedException; +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.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.net.ConnectException; +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 +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQConnection.class); + + private 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. + */ + private 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. + */ + private AMQProtocolHandler _protocolHandler; + + /** Maps from session id (Integer) to AMQSession instance */ + private final Map _sessions = new LinkedHashMap(); + + 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; + + private 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. + */ + private boolean _started; + + /** Policy dictating how to failover */ + private FailoverPolicy _failoverPolicy; + + /* + * _Connected should be refactored with a suitable wait object. + */ + private boolean _connected; + + /* + * The last error code that occured on the connection. Used to return the correct exception to the client + */ + private AMQException _lastAMQException = null; + + /* + * 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; + + /** + * @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); + } + + public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException + { + 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(); + } + + _failoverPolicy = new FailoverPolicy(connectionURL); + + _protocolHandler = new AMQProtocolHandler(this); + + // We are not currently connected + _connected = false; + + Exception lastException = new Exception(); + lastException.initCause(new ConnectException()); + + while (!_connected && _failoverPolicy.failoverAllowed()) + { + try + { + makeBrokerConnection(_failoverPolicy.getNextBrokerDetails()); + lastException = null; + _connected = true; + } + catch (Exception e) + { + lastException = e; + + //We need to change protocol handler here as an error during the connect will not + // cause the StateManager to be replaced. So the state is out of sync on reconnect + // This can be seen when a exception occurs during connection. i.e. log4j NoSuchMethod. (using < 1.2.12) + _protocolHandler.setStateManager(new AMQStateManager()); + + if (_logger.isInfoEnabled()) + { + _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(), + e.getCause()); + } + } + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Are we connected:" + _connected); + } + + if (!_connected) + { + String message = null; + + if (lastException != null) + { + if (lastException.getCause() != null) + { + message = lastException.getCause().getMessage(); + } + else + { + message = lastException.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:" + lastException.getClass(); + } + } + + AMQException e = new AMQConnectionFailureException(message); + + if (lastException != null) + { + if (lastException instanceof UnresolvedAddressException) + { + e = new AMQUnresolvedAddressException(message, _failoverPolicy.getCurrentBrokerDetails().toString()); + } + + e.initCause(lastException); + } + + throw e; + } + + _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)); + } + + 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.startsWith("/")) + { + virtualHost = virtualHost.substring(1); + } + + _virtualHost = virtualHost; + } + + private void makeBrokerConnection(BrokerDetails brokerDetail) throws IOException, AMQException + { + try + { + TransportConnection.getInstance(brokerDetail).connect(_protocolHandler, brokerDetail); + // this blocks until the connection has been set up or when an error + // has prevented the connection being set up + _protocolHandler.attainState(AMQState.CONNECTION_OPEN); + _failoverPolicy.attainedConnection(); + + // Again this should be changed to a suitable notify + _connected = true; + } + catch (AMQException e) + { + _lastAMQException = e; + throw e; + } + } + + 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; + } + + /** + * 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, AMQSession.DEFAULT_PREFETCH_HIGH_MARK); + } + + 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(); + + if (channelLimitReached()) + { + throw new ChannelLimitReachedException(_maximumChannelCount); + } + + return new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException + { + int channelId = _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(AMQConnection.this, channelId, transacted, acknowledgeMode, prefetchHigh, + prefetchLow); + // _protocolHandler.addSessionByChannel(channelId, session); + 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) + { + deregisterSession(channelId); + } + } + + if (_started) + { + try + { + session.start(); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + } + + return session; + } + }, this).execute(); + } + } + + private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException + { + + // TODO: Be aware of possible changes to parameter order as versions change. + + _protocolHandler.syncWrite(ChannelOpenBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), null), // outOfBand + ChannelOpenOkBody.class); + + // todo send low water mark when protocol allows. + // todo Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(BasicQosBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), false, // global + prefetchHigh, // prefetchCount + 0), // prefetchSize + BasicQosOkBody.class); + + if (transacted) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Issuing TxSelect for " + channelId); + } + + // TODO: Be aware of possible changes to parameter order as versions change. + _protocolHandler.syncWrite(TxSelectBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion()), 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("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)); + } + + private 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. + throw new IllegalStateException("Client name cannot be changed after being set"); + } + + 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) + { + final Iterator it = _sessions.entrySet().iterator(); + while (it.hasNext()) + { + final AMQSession s = (AMQSession) ((Map.Entry) it.next()).getValue(); + try + { + s.start(); + } + catch (AMQException e) + { + throw new JMSAMQException(e); + } + } + + _started = true; + } + } + + 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 + { + synchronized(_sessionCreationLock) + { + if(!sessions.isEmpty()) + { + AMQSession session = sessions.remove(0); + synchronized(session.getMessageDeliveryLock()) + { + close(sessions, timeout); + } + } + else + { + synchronized (getFailoverMutex()) + { + if (!_closed.getAndSet(true)) + { + try + { + long startCloseTime = System.currentTimeMillis(); + + _taskPool.shutdown(); + closeAllSessions(null, timeout, startCloseTime); + + 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); + + _protocolHandler.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) + { + 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 Map 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; + } + + /** + * 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 + { + if (cause instanceof AMQException) + { + je = + new JMSException(Integer.toString(((AMQException) cause).getErrorCode().getCode()), + "Exception thrown against " + toString() + ": " + cause); + } + else + { + je = new JMSException("Exception thrown against " + toString() + ": " + cause); + } + + if (cause instanceof Exception) + { + je.setLinkedException((Exception) cause); + } + } + + // 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) + { + _closed.set(true); + } + + if (_exceptionListener != null) + { + _exceptionListener.onException(je); + } + else + { + _logger.error("Throwable Received but no listener set: " + cause.getMessage()); + } + + if (!(cause instanceof AMQUndeliveredException) && !(cause instanceof AMQAuthenticationException)) + { + try + { + if (_logger.isInfoEnabled()) + { + _logger.info("Closing AMQConnection due to :" + cause.getMessage()); + } + + _closed.set(true); + 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()); + } + } + + void registerSession(int channelId, AMQSession session) + { + _sessions.put(channelId, session); + } + + void deregisterSession(int channelId) + { + _sessions.remove(channelId); + } + + /** + * 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(_sessions.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(); + } + } + + 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); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.java new file mode 100644 index 0000000000..7c0803a61a --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionFactory.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 java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Hashtable; +import java.util.UUID; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; +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 +{ + private String _host; + private int _port; + private String _defaultUsername; + private String _defaultPassword; + private String _virtualPath; + + private ConnectionURL _connectionDetails; + private SSLConfiguration _sslConfig; + + public AMQConnectionFactory() + { + } + + public AMQConnectionFactory(String url) throws URLSyntaxException + { + _connectionDetails = new AMQConnectionURL(url); + } + + public AMQConnectionFactory(ConnectionURL url) + { + _connectionDetails = url; + } + + 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 + "'")); + } + + public AMQConnectionFactory(String host, int port, String virtualPath) + { + this(host, port, "guest", "guest", virtualPath); + } + + 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; + } + + 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 + { + try + { + if (_connectionDetails != null) + { + _connectionDetails.setUsername(userName); + _connectionDetails.setPassword(password); + + if (_connectionDetails.getClientName() == null || _connectionDetails.getClientName().equals("")) + { + _connectionDetails.setClientName(getUniqueClientID()); + } + return new AMQConnection(_connectionDetails, _sslConfig); + } + else + { + return new AMQConnection(_host, _port, userName, password, 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(new AMQConnectionURL((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 + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java new file mode 100644 index 0000000000..24f5ead2d0 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQConnectionURL.java @@ -0,0 +1,455 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; + +public class AMQConnectionURL implements ConnectionURL +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQConnectionURL.class); + + private String _url; + private String _failoverMethod; + private HashMap _failoverOptions; + private HashMap _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 + { + _url = fullURL; + _options = new HashMap(); + _brokers = new LinkedList(); + _failoverOptions = new HashMap(); + + // 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(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 + { + setClientName(uid); + } + + } + else + { + 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(AMQ_PROTOCOL.length() + 3, "User information not found on url", fullURL); + } + else + { + parseUserInfo(userInfo); + } + + String virtualHost = connection.getPath(); + + if ((virtualHost != null) && (!virtualHost.equals(""))) + { + setVirtualHost(virtualHost); + } + else + { + int authLength = connection.getAuthority().length(); + int start = 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(_options, 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(AMQ_PROTOCOL.length() + 3, userinfo.length(), + "Null password in user information not allowed.", _url); + } + else + { + setUsername(userinfo.substring(0, colonIndex)); + setPassword(userinfo.substring(colonIndex + 1)); + } + + } + + private void processOptions() throws URLSyntaxException + { + if (_options.containsKey(OPTIONS_BROKERLIST)) + { + String brokerlist = _options.get(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(); + + _brokers.add(new AMQBrokerDetails(broker)); + } + + _options.remove(OPTIONS_BROKERLIST); + } + + if (_options.containsKey(OPTIONS_FAILOVER)) + { + String failover = _options.get(OPTIONS_FAILOVER); + + // failover='method?option='value',option='value'' + + int methodIndex = failover.indexOf('?'); + + if (methodIndex > -1) + { + _failoverMethod = failover.substring(0, methodIndex); + URLHelper.parseOptions(_failoverOptions, failover.substring(methodIndex + 1)); + } + else + { + _failoverMethod = failover; + } + + _options.remove(OPTIONS_FAILOVER); + } + + if (_options.containsKey(OPTIONS_DEFAULT_TOPIC_EXCHANGE)) + { + _defaultTopicExchangeName = new AMQShortString(_options.get(OPTIONS_DEFAULT_TOPIC_EXCHANGE)); + } + + if (_options.containsKey(OPTIONS_DEFAULT_QUEUE_EXCHANGE)) + { + _defaultQueueExchangeName = new AMQShortString(_options.get(OPTIONS_DEFAULT_QUEUE_EXCHANGE)); + } + + if (_options.containsKey(OPTIONS_TEMPORARY_QUEUE_EXCHANGE)) + { + _temporaryQueueExchangeName = new AMQShortString(_options.get(OPTIONS_TEMPORARY_QUEUE_EXCHANGE)); + } + + if (_options.containsKey(OPTIONS_TEMPORARY_TOPIC_EXCHANGE)) + { + _temporaryTopicExchangeName = new AMQShortString(_options.get(OPTIONS_TEMPORARY_TOPIC_EXCHANGE)); + } + } + + public String getURL() + { + return _url; + } + + public String getFailoverMethod() + { + return _failoverMethod; + } + + 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 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 AMQShortString getDefaultTopicExchangeName() + { + return _defaultTopicExchangeName; + } + + public AMQShortString getTemporaryQueueExchangeName() + { + return _temporaryQueueExchangeName; + } + + public AMQShortString getTemporaryTopicExchangeName() + { + return _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(':'); + if (_logger.isDebugEnabled()) + { + sb.append(_password); + } + else + { + 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?brokerlist='tcp://localhost:5672;jcp://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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java new file mode 100644 index 0000000000..cc5af07b20 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -0,0 +1,451 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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; +import org.apache.qpid.url.URLSyntaxException; + + +public abstract class AMQDestination implements Destination, Referenceable +{ + protected final AMQShortString _exchangeName; + + protected final AMQShortString _exchangeClass; + + protected final AMQShortString _destinationName; + + protected final boolean _isDurable; + + protected final boolean _isExclusive; + + protected final boolean _isAutoDelete; + + private AMQShortString _queueName; + + private String _url; + private AMQShortString _urlAsShortString; + + private boolean _validated; + + 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 Integer QUEUE_TYPE = Integer.valueOf(1); + public static final Integer TOPIC_TYPE = Integer.valueOf(2); + public static final Integer UNKNOWN_TYPE = Integer.valueOf(3); + + protected AMQDestination(String url) throws URLSyntaxException + { + this(new AMQBindingURL(url)); + } + + protected AMQDestination(BindingURL binding) + { + _exchangeName = binding.getExchangeName(); + _exchangeClass = binding.getExchangeClass(); + _destinationName = binding.getDestinationName(); + + _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 : new AMQShortString(binding.getQueueName()); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, AMQShortString queueName) + { + this(exchangeName, exchangeClass, destinationName, false, false, queueName); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName) + { + this(exchangeName, exchangeClass, destinationName, false, false, null); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName) + { + this(exchangeName, exchangeClass, destinationName, isExclusive, isAutoDelete, queueName, false); + } + + protected AMQDestination(AMQShortString exchangeName, AMQShortString exchangeClass, AMQShortString destinationName, boolean isExclusive, + boolean isAutoDelete, AMQShortString queueName, boolean isDurable) + { + if (destinationName == null) + { + throw new IllegalArgumentException("Destination exchange must not be null"); + } + if (exchangeName == null) + { + throw new IllegalArgumentException("Exchange exchange must not be null"); + } + if (exchangeClass == null) + { + throw new IllegalArgumentException("Exchange class must not be null"); + } + _exchangeName = exchangeName; + _exchangeClass = exchangeClass; + _destinationName = destinationName; + _isExclusive = isExclusive; + _isAutoDelete = isAutoDelete; + _queueName = queueName; + _isDurable = isDurable; + } + + 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 AMQShortString getDestinationName() + { + return _destinationName; + } + + 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 abstract AMQShortString getRoutingKey(); + + public boolean isExclusive() + { + return _isExclusive; + } + + public boolean isAutoDelete() + { + return _isAutoDelete; + } + + public abstract boolean isNameRequired(); + + public String toString() + { + return toURL(); + + } + + public boolean isValidated() + { + return _validated; + } + + public void setValidated(boolean validated) + { + _validated = validated; + } + + public String toURL() + { + String url = _url; + if(url == null) + { + + + StringBuffer sb = new StringBuffer(); + + sb.append(_exchangeClass); + sb.append("://"); + sb.append(_exchangeName); + + sb.append('/'); + + if (_destinationName != null) + { + sb.append(_destinationName); + } + + sb.append('/'); + + if (_queueName != null) + { + sb.append(_queueName); + } + + sb.append('?'); + + 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 + + (_destinationName == null ? 0 : _destinationName.length()) + 1 + + (_queueName == null ? 0 : _queueName.length()) + 1 + + 1; + encoding = new byte[size]; + int pos = 0; + + pos = _exchangeClass.writeToByteArray(encoding, pos); + pos = _exchangeName.writeToByteArray(encoding, pos); + if(_destinationName == null) + { + encoding[pos++] = (byte)0; + } + else + { + pos = _destinationName.writeToByteArray(encoding,pos); + } + 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 (!_destinationName.equals(that._destinationName)) + { + return false; + } + 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 destinationName; + 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; + destinationName = AMQShortString.readFromByteArray(byteEncodedDestination, pos); + pos+= (destinationName == null ? 0 : destinationName.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,destinationName,queueName,isExclusive,isAutoDelete,isDurable); + } + else if (exchangeClass.equals(ExchangeDefaults.TOPIC_EXCHANGE_CLASS)) + { + return new AMQTopic(exchangeName,destinationName,isAutoDelete,queueName,isDurable); + } + else if (exchangeClass.equals(ExchangeDefaults.HEADERS_EXCHANGE_CLASS)) + { + return new AMQHeadersExchange(destinationName); + } + 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 + { + throw new IllegalArgumentException("Unknown Exchange Class:" + type + " in binding:" + binding); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.java new file mode 100644 index 0000000000..1a2fe0d355 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQHeadersExchange.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; + +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 AMQShortString getRoutingKey() + { + return getDestinationName(); + } + + 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 false; + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoConsumersException.java new file mode 100644 index 0000000000..54d5a0426f --- /dev/null +++ b/Final2/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) + { + super(AMQConstant.NO_CONSUMERS, msg, bounced); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQNoRouteException.java new file mode 100644 index 0000000000..a314101acf --- /dev/null +++ b/Final2/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) + { + super(AMQConstant.NO_ROUTE, msg, bounced); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueue.java new file mode 100644 index 0000000000..9185bc87e8 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueue.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; + +import javax.jms.Queue; +import javax.jms.Connection; + +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); } + + + /** + * 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 destinationName the queue name + * @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 destinationName, AMQShortString queueName, boolean exclusive, boolean autoDelete) + { + this(exchangeName, destinationName, queueName, exclusive, autoDelete, false); + } + + + public AMQQueue(AMQShortString exchangeName, AMQShortString destinationName, AMQShortString queueName, boolean exclusive, boolean autoDelete, boolean durable) + { + super(exchangeName, ExchangeDefaults.DIRECT_EXCHANGE_CLASS, destinationName, exclusive, + autoDelete, queueName, durable); + } + + + + public AMQShortString getRoutingKey() + { + return getAMQQueueName(); + } + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java new file mode 100644 index 0000000000..28e5992b26 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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); + 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.receive(); + + 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(); + _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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQQueueSessionAdaptor.java new file mode 100644 index 0000000000..a8c83d8868 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQSession.java new file mode 100644 index 0000000000..a0b79b135d --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -0,0 +1,2800 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQInvalidArgumentException; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.AMQDisconnectedException; +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.AbstractJMSMessage; +import org.apache.qpid.client.message.JMSBytesMessage; +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.message.JMSObjectMessage; +import org.apache.qpid.client.message.JMSStreamMessage; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.MessageFactoryRegistry; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.util.FlowControllingBlockingQueue; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicAckBody; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicConsumeOkBody; +import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.BasicRecoverOkBody; +import org.apache.qpid.framing.BasicRejectBody; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ChannelFlowBody; +import org.apache.qpid.framing.ChannelFlowOkBody; +import org.apache.qpid.framing.ExchangeBoundBody; +import org.apache.qpid.framing.ExchangeBoundOkBody; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueBindOkBody; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.QueueDeclareOkBody; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.framing.TxCommitBody; +import org.apache.qpid.framing.TxCommitOkBody; +import org.apache.qpid.framing.TxRollbackBody; +import org.apache.qpid.framing.TxRollbackOkBody; +import org.apache.qpid.jms.Session; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +/** + *

+ *
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 class AMQSession extends Closeable implements Session, QueueSession, TopicSession +{ + /** Used for debugging. */ + private static final Logger _logger = LoggerFactory.getLogger(AMQSession.class); + + /** Used for debugging in the dispatcher. */ + private static final Logger _dispatcherLogger = LoggerFactory.getLogger(Dispatcher.class); + + /** The default maximum number of prefetched message at which to suspend the channel. */ + public static final int DEFAULT_PREFETCH_HIGH_MARK = 5000; + + /** The default minimum number of prefetched messages at which to resume the channel. */ + public static final int DEFAULT_PREFETCH_LOW_MARK = 2500; + + /** + * 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. */ + private AMQConnection _connection; + + /** Used to indicate whether or not this is a transactional session. */ + private boolean _transacted; + + /** Holds the sessions acknowledgement mode. */ + private int _acknowledgeMode; + + /** Holds this session unique identifier, used to distinguish it from other sessions. */ + private int _channelId; + + /** @todo This does not appear to be set? */ + private int _ticket; + + /** Holds the high mark for prefetched message, at which the session is suspended. */ + private int _defaultPrefetchHighMark = DEFAULT_PREFETCH_HIGH_MARK; + + /** Holds the low mark for prefetched messages, below which the session is resumed. */ + private int _defaultPrefetchLowMark = DEFAULT_PREFETCH_LOW_MARK; + + /** 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. + */ + private 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. + */ + private final ConcurrentHashMap _reverseSubscriptionMap = + new ConcurrentHashMap(); + + /** + * Used to hold incoming messages. + * + * @todo Weaken the type once {@link FlowControllingBlockingQueue} implements Queue. + */ + private final FlowControllingBlockingQueue _queue; + + /** Holds the highest received delivery tag. */ + private final AtomicLong _highestDeliveryTag = new AtomicLong(-1); + + /** Holds the dispatcher thread for this session. */ + private Dispatcher _dispatcher; + + /** Holds the message factory factory for this session. */ + private 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. + */ + private Map _consumers = + new ConcurrentHashMap(); + + /** 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. + */ + private final AtomicBoolean _firstDispatcher = new AtomicBoolean(true); + + /** Used to indicate that the session should start pre-fetching messages as soon as it is started. */ + private final boolean _immediatePrefetch; + + /** Indicates that warnings should be generated on violations of the strict AMQP. */ + private final boolean _strictAMQP; + + /** Indicates that runtime exceptions should be generated on vilations of the strict AMQP. */ + private final boolean _strictAMQPFATAL; + private final Object _messageDeliveryLock = new Object(); + + /** + * 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(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() + { + public void aboveThreshold(int currentValue) + { + if (_acknowledgeMode == NO_ACKNOWLEDGE) + { + _logger.debug( + "Above threshold(" + _defaultPrefetchHighMark + + ") so suspending channel. Current value is " + currentValue); + new Thread(new SuspenderRunner(true)).start(); + } + } + + public void underThreshold(int currentValue) + { + if (_acknowledgeMode == NO_ACKNOWLEDGE) + { + _logger.debug( + "Below threshold(" + _defaultPrefetchLowMark + + ") so unsuspending channel. Current value is " + currentValue); + new Thread(new SuspenderRunner(false)).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 BytesMessage createBytesMessage() throws JMSException + { + checkNotClosed(); + return new JMSBytesMessage(); + } + + /** + * 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"); + } + + for (BasicMessageConsumer consumer : _consumers.values()) + { + consumer.acknowledge(); + } + } + + /** + * 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 void acknowledgeMessage(long deliveryTag, boolean multiple) + { + final AMQFrame ackFrame = + BasicAckBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, + multiple); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Sending ack for delivery tag " + deliveryTag + " on channel " + _channelId); + } + + getProtocolHandler().writeFrame(ackFrame); + } + + /** + * 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) throws AMQException + { + /*new FailoverRetrySupport(new FailoverProtectedOperation()*/ + new FailoverNoopSupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + AMQFrame queueBind = + QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + arguments, // arguments + exchangeName, // exchange + false, // nowait + queueName, // queue + routingKey, // routingKey + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class); + + return null; + } + }, _connection).execute(); + } + + /** + * 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)); + } + + synchronized (_connection.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) + { + // Ensure we only try and close an open session. + if (!_closed.getAndSet(true)) + { + // we pass null since this is not an error case + closeProducersAndConsumers(null); + + try + { + + getProtocolHandler().closeSession(this); + + final AMQFrame frame = + ChannelCloseBody.createAMQFrame(getChannelId(), getProtocolMajorVersion(), getProtocolMinorVersion(), + 0, // classId + 0, // methodId + AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client closing channel")); // replyText + + getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout); + + // When control resumes at this point, a reply will have been received that + // indicates the broker has closed the channel successfully. + } + 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); + } + } + } + } + } + + /** + * 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 + { + synchronized (_connection.getFailoverMutex()) + { + if (e instanceof AMQDisconnectedException) + { + if (_dispatcher != null) + { + // Failover failed and ain't coming back. Knife the dispatcher. + _dispatcher.interrupt(); + } + } + 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 + _closed.set(true); + 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 + { + // Acknowledge up to message last delivered (if any) for each consumer. + // need to send ack for messages delivered to consumers so far + for (Iterator i = _consumers.values().iterator(); i.hasNext();) + { + // Sends acknowledgement to server + i.next().acknowledgeLastDelivered(); + } + + // Commits outstanding messages sent and outstanding acknowledgements. + final AMQProtocolHandler handler = getProtocolHandler(); + + handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()), + TxCommitOkBody.class); + } + 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 void confirmConsumerCancelled(AMQShortString consumerTag) + { + + // Remove the consumer from the map + BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag); + if (consumer != null) + { + // fixme this isn't right.. needs to check if _queue contains data for this consumer + if (consumer.isAutoClose()) // && _queue.isEmpty()) + { + consumer.closeWhenNoMessages(true); + } + + if (!consumer.isNoConsume()) + { + // 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"); + startDistpatcherIfNecessary(true); + } + + _dispatcher.rejectPending(consumer); + } + else + { + // 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(); + } + } + else + { + _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map."); + } + + } + + 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, false, null, null, + false, false); + } + + public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, + messageSelector, null, false, false); + } + + public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) + throws JMSException + { + checkValidDestination(destination); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + 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, 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, 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 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).getDestinationName(); + } + 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; + } + + /** Note, currently this does not handle reuse of the same name with different topics correctly. */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) + throws JMSException + { + checkNotClosed(); + checkValidTopic(topic); + AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection); + BasicMessageConsumer consumer = (BasicMessageConsumer) 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(); + } + + public javax.jms.Message createMessage() throws JMSException + { + return createBytesMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException + { + checkNotClosed(); + return (ObjectMessage) new JMSObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable object) throws JMSException + { + ObjectMessage msg = createObjectMessage(); + msg.setObject(object); + + return msg; + } + + public BasicMessageProducer createProducer(Destination destination) throws JMSException + { + return createProducerImpl(destination, DEFAULT_MANDATORY, DEFAULT_IMMEDIATE); + } + + public BasicMessageProducer createProducer(Destination destination, boolean immediate) throws JMSException + { + return createProducerImpl(destination, DEFAULT_MANDATORY, immediate); + } + + public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate) + throws JMSException + { + return createProducerImpl(destination, mandatory, immediate); + } + + public BasicMessageProducer 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((BasicMessageProducer) createProducer(topic), 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 (URLSyntaxException 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 + { + new FailoverRetrySupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + AMQFrame queueDeclare = + QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + autoDelete, // autoDelete + durable, // durable + exclusive, // exclusive + false, // nowait + false, // passive + name, // queue + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class); + + return null; + } + }, _connection).execute(); + } + + /** + * 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; + BasicMessageConsumer consumer = (BasicMessageConsumer) 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; + BasicMessageConsumer consumer = (BasicMessageConsumer) 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; + BasicMessageConsumer consumer = (BasicMessageConsumer) 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; + BasicMessageConsumer consumer = (BasicMessageConsumer) 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 + { + synchronized (_connection.getFailoverMutex()) + { + checkNotClosed(); + + return new JMSStreamMessage(); + } + } + + /** + * 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, (BasicMessageConsumer) createConsumer(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, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal)); + } + + public TemporaryQueue createTemporaryQueue() throws JMSException + { + checkNotClosed(); + + return new AMQTemporaryQueue(this); + } + + public TemporaryTopic createTemporaryTopic() throws JMSException + { + checkNotClosed(); + + return new AMQTemporaryTopic(this); + } + + public TextMessage createTextMessage() throws JMSException + { + synchronized (_connection.getFailoverMutex()) + { + checkNotClosed(); + + return new JMSTextMessage(); + } + } + + 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 (URLSyntaxException 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; + } + + /** + * 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.getDeliverBody() == null) ? ("B:" + message.getBounceBody()) : ("D:" + message.getDeliverBody())) + + "] received in session with channel id " + _channelId); + } + + if (message.getDeliverBody() == null) + { + // Return of the bounced message. + returnBouncedMessage(message); + } + else + { + _highestDeliveryTag.set(message.getDeliverBody().deliveryTag); + _queue.add(message); + } + } + + /** + * 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); + } + + for (BasicMessageConsumer consumer : _consumers.values()) + { + consumer.clearUnackedMessages(); + } + + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + + if (isStrictAMQP()) + { + // We can't use the BasicRecoverBody-OK method as it isn't part of the spec. + _connection.getProtocolHandler().writeFrame(BasicRecoverBody.createAMQFrame(_channelId, + getProtocolMajorVersion(), getProtocolMinorVersion(), false)); // requeue + _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order."); + } + else + { + + _connection.getProtocolHandler().syncWrite( + BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false) // requeue + , BasicRecoverOkBody.class); + } + + 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); + } + } + + public void rejectMessage(UnprocessedMessage message, boolean requeue) + { + + if (_logger.isTraceEnabled()) + { + _logger.trace("Rejecting Unacked message:" + message.getDeliverBody().deliveryTag); + } + + rejectMessage(message.getDeliverBody().deliveryTag, requeue); + } + + public void rejectMessage(AbstractJMSMessage message, boolean requeue) + { + if (_logger.isTraceEnabled()) + { + _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag()); + } + + rejectMessage(message.getDeliveryTag(), requeue); + + } + + 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()); + } + + AMQFrame basicRejectBody = + BasicRejectBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, + requeue); + + _connection.getProtocolHandler().writeFrame(basicRejectBody); + } + } + + /** + * 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); + } + + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + + _connection.getProtocolHandler().syncWrite(TxRollbackBody.createAMQFrame(_channelId, + getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class); + + 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 void run() + { + throw new java.lang.UnsupportedOperationException(); + } + + public void setMessageListener(MessageListener listener) throws JMSException + { + // checkNotClosed(); + // + // if (_dispatcher != null && !_dispatcher.connectionStopped()) + // { + // throw new javax.jms.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.jms.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 + { + + if (isQueueBound(getDefaultTopicExchangeName(), AMQTopic.getDurableTopicQueueName(name, _connection))) + { + deleteQueue(AMQTopic.getDurableTopicQueueName(name, _connection)); + } + else + { + throw new InvalidDestinationException("Unknown subscription exchange:" + name); + } + } + } + } + + protected MessageConsumer 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 MessageConsumer 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()); + if (rawSelector != null) + { + ft.addAll(rawSelector); + } + + BasicMessageConsumer consumer = + new BasicMessageConsumer(_channelId, _connection, amqd, messageSelector, noLocal, + _messageFactoryRegistry, AMQSession.this, protocolHandler, ft, prefetchHigh, prefetchLow, + exclusive, _acknowledgeMode, 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); + + if (_logger.isDebugEnabled()) + { + e.printStackTrace(); + } + + ex.setLinkedException(e); + throw ex; + } + + synchronized (destination) + { + _destinationConsumerCount.putIfAbsent(destination, new AtomicInteger()); + _destinationConsumerCount.get(destination).incrementAndGet(); + } + + return consumer; + } + }, _connection).execute(); + } + + /** + * Called by the MessageConsumer when closing, to deregister the consumer from the map from consumerTag to consumer + * instance. + * + * @param consumer the consum + */ + void deregisterConsumer(BasicMessageConsumer 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); + } + } + } + } + + 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. + */ + 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 = + ExchangeBoundBody.createAMQFrame(_channelId, getProtocolMajorVersion(), + getProtocolMinorVersion(), exchangeName, // exchange + queueName, // queue + routingKey); // routingKey + + return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); + + } + }, _connection).execute(); + + // Extract and return the response code from the query. + ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); + + return (responseBody.replyCode == 0); + } + catch (AMQException e) + { + throw new JMSAMQException("Queue bound query failed: " + e.getMessage(), e); + } + } + + /** + * 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 + { + 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()) + { + startDistpatcherIfNecessary(); + } + } + + void startDistpatcherIfNecessary() + { + //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); + } + } + } + + startDistpatcherIfNecessary(false); + } + + synchronized void startDistpatcherIfNecessary(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"); + } + } + } + + private 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 + */ + private 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 BasicMessageConsumer 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 BasicMessageProducer prod = (BasicMessageProducer) 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(BasicMessageConsumer consumer, AMQShortString queueName, + AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException + { + // need to generate a consumer tag on the client so we can exploit the nowait flag + AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++)); + + 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); + } + + consumer.setConsumerTag(tag); + // we must register the consumer in the map before we actually start listening + _consumers.put(tag, consumer); + + try + { + // TODO: Be aware of possible changes to parameter order as versions change. + AMQFrame jmsConsume = + BasicConsumeBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments + tag, // consumerTag + consumer.isExclusive(), // exclusive + consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck + consumer.isNoLocal(), // noLocal + nowait, // nowait + queueName, // queue + getTicket()); // ticket + + if (nowait) + { + protocolHandler.writeFrame(jmsConsume); + } + else + { + protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class); + } + } + catch (AMQException e) + { + // clean-up the map in the event of an error + _consumers.remove(tag); + throw e; + } + } + + private BasicMessageProducer createProducerImpl(Destination destination, boolean mandatory, boolean immediate) + throws JMSException + { + return createProducerImpl(destination, mandatory, immediate, false); + } + + private BasicMessageProducer createProducerImpl(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent) throws JMSException + { + return new FailoverRetrySupport( + new FailoverProtectedOperation() + { + public BasicMessageProducer execute() throws JMSException, FailoverException + { + checkNotClosed(); + long producerId = getNextProducerId(); + BasicMessageProducer producer = + new BasicMessageProducer(_connection, (AMQDestination) destination, _transacted, _channelId, + AMQSession.this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + registerProducer(producerId, producer); + + return producer; + } + }, _connection).execute(); + } + + private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException + { + declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait); + } + + /** + * 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 + { + AMQFrame exchangeDeclare = + ExchangeDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + false, // autoDelete + false, // durable + name, // exchange + false, // internal + nowait, // nowait + false, // passive + getTicket(), // ticket + type); // type + + protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + + return null; + } + }, _connection).execute(); + } + + /** + * 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. + */ + private AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) + 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()); + } + + AMQFrame queueDeclare = + QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + amqd.isAutoDelete(), // autoDelete + amqd.isDurable(), // durable + amqd.isExclusive(), // exclusive + false, // nowait + false, // passive + amqd.getAMQQueueName(), // queue + getTicket()); // ticket + + protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class); + + return amqd.getAMQQueueName(); + } + }, _connection).execute(); + } + + /** + * 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. + */ + private void deleteQueue(final AMQShortString queueName) throws JMSException + { + try + { + new FailoverRetrySupport(new FailoverProtectedOperation() + { + public Object execute() throws AMQException, FailoverException + { + AMQFrame queueDeleteFrame = + QueueDeleteBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + false, // ifEmpty + false, // ifUnused + true, // nowait + queueName, // queue + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); + + return null; + } + }, _connection).execute(); + } + catch (AMQException e) + { + throw new JMSAMQException("The queue deletion failed: " + e.getMessage(), e); + } + } + + private long getNextProducerId() + { + return ++_nextProducerId; + } + + private AMQProtocolHandler getProtocolHandler() + { + return _connection.getProtocolHandler(); + } + + private byte getProtocolMajorVersion() + { + return getProtocolHandler().getProtocolMajorVersion(); + } + + private byte getProtocolMinorVersion() + { + return getProtocolHandler().getProtocolMinorVersion(); + } + + private 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 BasicMessageConsumer 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); + } + } + + public void declareAndBind(AMQDestination amqd) + throws + AMQException + { + AMQProtocolHandler protocolHandler = getProtocolHandler(); + declareExchange(amqd, protocolHandler, false); + AMQShortString queueName = declareQueue(amqd, protocolHandler); + bindQueue(queueName, amqd.getRoutingKey(), new FieldTable(), amqd.getExchangeName()); + } + + /** + * Callers must hold the failover mutex before calling this method. + * + * @param consumer + * + * @throws AMQException + */ + private void registerConsumer(BasicMessageConsumer consumer, boolean nowait) throws AMQException // , FailoverException + { + AMQDestination amqd = consumer.getDestination(); + + AMQProtocolHandler protocolHandler = getProtocolHandler(); + + declareExchange(amqd, protocolHandler, false); + + AMQShortString queueName = declareQueue(amqd, protocolHandler); + + // bindQueue(amqd, queueName, protocolHandler, consumer.getRawSelectorFieldTable()); + bindQueue(queueName, amqd.getRoutingKey(), consumer.getRawSelectorFieldTable(), amqd.getExchangeName()); + + // 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(e.getMessage(), e); + } + catch (FailoverException e) + { + throw new AMQException("Fail-over exception interrupted basic consume.", e); + } + } + + private void registerProducer(long producerId, MessageProducer producer) + { + _producers.put(new Long(producerId), producer); + } + + /** + * @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) + */ + + private void rejectMessagesForConsumerTag(AMQShortString consumerTag, boolean requeue) + { + 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 ((consumerTag == null) || message.getDeliverBody().consumerTag.equals(consumerTag)) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:" + + message.getDeliverBody().deliveryTag); + } + + messages.remove(); + + rejectMessage(message, requeue); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejected the message(" + message.getDeliverBody() + ") for consumer :" + consumerTag); + } + } + } + } + + private void resubscribeConsumers() throws AMQException + { + ArrayList consumers = new ArrayList(_consumers.values()); + _consumers.clear(); + + for (Iterator it = consumers.iterator(); it.hasNext();) + { + BasicMessageConsumer consumer = (BasicMessageConsumer) it.next(); + 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();) + { + BasicMessageProducer producer = (BasicMessageProducer) it.next(); + producer.resubscribe(); + } + } + + private void returnBouncedMessage(final UnprocessedMessage message) + { + _connection.performConnectionTask(new Runnable() + { + public void run() + { + try + { + // Bounced message is processed here, away from the mina thread + AbstractJMSMessage bouncedMessage = + _messageFactoryRegistry.createMessage(0, false, message.getBounceBody().exchange, + message.getBounceBody().routingKey, message.getContentHeader(), message.getBodies()); + + AMQConstant errorCode = AMQConstant.getConstant(message.getBounceBody().replyCode); + AMQShortString reason = message.getBounceBody().replyText; + _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)); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + _connection.exceptionReceived(new AMQNoRouteException("Error: " + reason, bouncedMessage)); + } + else + { + _connection.exceptionReceived( + new AMQUndeliveredException(errorCode, "Error: " + reason, bouncedMessage)); + } + + } + catch (Exception e) + { + _logger.error( + "Caught exception trying to raise undelivered message exception (dump follows) - ignoring...", + e); + } + } + }); + } + + /** + * 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. + */ + private void suspendChannel(boolean suspend) throws AMQException // , FailoverException + { + synchronized (_suspensionLock) + { + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Setting channel flow : " + (suspend ? "suspended" : "unsuspended")); + } + + _suspended = suspend; + + AMQFrame channelFlowFrame = + ChannelFlowBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + !suspend); + + _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class); + } + catch (FailoverException e) + { + throw new AMQException("Fail-over interrupted suspend/unsuspend channel.", e); + } + } + } + + Object getMessageDeliveryLock() + { + return _messageDeliveryLock; + } + + /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ + private class Dispatcher extends Thread + { + + /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */ + private final AtomicBoolean _dispatcherClosed = new AtomicBoolean(false); + + private final Object _lock = new Object(); + private final AtomicLong _rollbackMark = new AtomicLong(-1); + private String dispatcherID = "" + System.identityHashCode(this); + + public Dispatcher() + { + super("Dispatcher-Channel-" + _channelId); + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " created"); + } + } + + public void close() + { + _dispatcherClosed.set(true); + interrupt(); + + // fixme awaitTermination + + } + + public void rejectPending(BasicMessageConsumer consumer) + { + synchronized (_lock) + { + boolean stopped = _dispatcher.connectionStopped(); + + if (!stopped) + { + _dispatcher.setConnectionStopped(true); + } + + // Reject messages on pre-receive queue + consumer.rollback(); + + // Reject messages on pre-dispatch queue + rejectMessagesForConsumerTag(consumer.getConsumerTag(), true); + //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 (BasicMessageConsumer consumer : _consumers.values()) + { + if (!consumer.isNoConsume()) + { + consumer.rollback(); + } + else + { + // cClear the _SQ here. + consumer.clearReceiveQueue(); + } + + } + + 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(2000); + } + catch (InterruptedException e) + { + // ignore + } + } + } + + try + { + while (!_dispatcherClosed.get()) + { + message = (UnprocessedMessage) _queue.poll(1000, TimeUnit.MILLISECONDS); + if (message != null) + { + synchronized (_lock) + { + + while (connectionStopped()) + { + _lock.wait(2000); + } + + if (message.getDeliverBody().deliveryTag <= _rollbackMark.get()) + { + rejectMessage(message, true); + } + else + { + synchronized (_messageDeliveryLock) + { + dispatchMessage(message); + } + } + + } + } + } + } + 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) + { + if (message.getDeliverBody() != null) + { + final BasicMessageConsumer consumer = + (BasicMessageConsumer) _consumers.get(message.getDeliverBody().consumerTag); + + if ((consumer == null) || consumer.isClosed()) + { + if (_dispatcherLogger.isInfoEnabled()) + { + if (consumer == null) + { + _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliverBody().deliveryTag + "] from queue " + + message.getDeliverBody().consumerTag + " )without a handler - rejecting(requeue)..."); + } + else + { + _dispatcherLogger.info("Dispatcher(" + dispatcherID + ")Received a message(" + System.identityHashCode(message) + ") [" + + message.getDeliverBody().deliveryTag + "] from queue consumer(" + + consumer.debugIdentity() + ") is closed rejecting(requeue)..."); + } + } + // Don't reject if we're already closing + if (!_dispatcherClosed.get()) + { + rejectMessage(message, true); + } + } + else + { + consumer.notifyMessage(message, _channelId); + } + } + } + } + + /*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 boolean _suspend; + + public SuspenderRunner(boolean suspend) + { + _suspend = suspend; + } + + public void run() + { + try + { + suspendChannel(_suspend); + } + catch (AMQException e) + { + _logger.warn("Unable to suspend channel"); + } + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQSessionAdapter.java new file mode 100644 index 0000000000..93f10761e2 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryQueue.java new file mode 100644 index 0000000000..f54cb782c8 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTemporaryTopic.java new file mode 100644 index 0000000000..7b5781530b --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java new file mode 100644 index 0000000000..319e728edf --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopic.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.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(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); + } + + public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection) + throws JMSException + { + return new AMQTopic(topic.getExchangeName(), topic.getDestinationName(), 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.getDestinationName().toString(); + } + + public AMQShortString getRoutingKey() + { + return getDestinationName(); + } + + public boolean isNameRequired() + { + // Topics always rely on a server generated queue name. + return false; + } + + /** + * 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) + { + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQTopicSessionAdaptor.java new file mode 100644 index 0000000000..f44f8414fa --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.java new file mode 100644 index 0000000000..0f3723c58b --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/AMQUndefinedDestination.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.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 AMQShortString getRoutingKey() + { + return getDestinationName(); + } + + public boolean isNameRequired() + { + return getAMQQueueName() == null; + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java new file mode 100644 index 0000000000..ddaf0cfd93 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -0,0 +1,996 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AbstractJMSMessage; +import org.apache.qpid.client.message.MessageFactoryRegistry; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.framing.FieldTable; +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.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class BasicMessageConsumer extends Closeable implements MessageConsumer +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class); + + /** The connection being used by this consumer */ + private AMQConnection _connection; + + private String _messageSelector; + + private boolean _noLocal; + + private 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 */ + private AMQShortString _consumerTag; + + /** We need to know the channel id when constructing frames */ + private 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 + */ + private final ArrayBlockingQueue _synchronousQueue; + + private MessageFactoryRegistry _messageFactory; + + private final AMQSession _session; + + private AMQProtocolHandler _protocolHandler; + + /** We need to store the "raw" field table so that we can resubscribe in the event of failover being required */ + private FieldTable _rawSelectorFieldTable; + + /** + * We store the high water prefetch field in order to be able to reuse it when resubscribing in the event of + * failover + */ + private 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 int _prefetchLow; + + /** We store the exclusive field in order to be able to reuse it when resubscribing in the event of failover */ + private 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. + */ + private 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; + + private ConcurrentLinkedQueue _unacknowledgedDeliveryTags = new ConcurrentLinkedQueue(); + + /** List of tags delievered, The last of which which should be acknowledged on commit in transaction mode. */ + private ConcurrentLinkedQueue _receivedDeliveryTags = new ConcurrentLinkedQueue(); + + /** + * 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; + + /** + * 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 boolean _autoClose; + private boolean _closeWhenNoMessages; + + private 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 rawSelectorFieldTable, 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; + _rawSelectorFieldTable = rawSelectorFieldTable; + _prefetchHigh = prefetchHigh; + _prefetchLow = prefetchLow; + _exclusive = exclusive; + _acknowledgeMode = acknowledgeMode; + _synchronousQueue = new ArrayBlockingQueue(prefetchHigh, true); + _autoClose = autoClose; + _noConsume = noConsume; + + // Force queue browsers not to use acknowledge modes. + if (_noConsume) + { + _acknowledgeMode = Session.NO_ACKNOWLEDGE; + } + } + + 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; + } + + private 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 + + _messageListener.set(messageListener); + _session.setHasMessageListeners(); + _session.startDistpatcherIfNecessary(); + } + } + } + + private void preApplicationProcessing(AbstractJMSMessage msg) throws JMSException + { + + switch (_acknowledgeMode) + { + + case Session.CLIENT_ACKNOWLEDGE: + _unacknowledgedDeliveryTags.add(msg.getDeliveryTag()); + break; + + case Session.SESSION_TRANSACTED: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + else + { + _logger.info("Recording tag for commit:" + msg.getDeliveryTag()); + _receivedDeliveryTags.add(msg.getDeliveryTag()); + } + + break; + } + + _session.setInRecovery(false); + } + + private void acquireReceiving() throws JMSException + { + 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(); + } + + private void releaseReceiving() + { + _receiving.set(false); + _receivingThread = null; + } + + public FieldTable getRawSelectorFieldTable() + { + return _rawSelectorFieldTable; + } + + 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(); + + acquireReceiving(); + + _session.startDistpatcherIfNecessary(); + + try + { + if (closeOnAutoClose()) + { + return null; + } + + Object o = null; + if (l > 0) + { + long endtime = System.currentTimeMillis() + l; + while (System.currentTimeMillis() < endtime && o == null) + { + try + { + o = _synchronousQueue.poll(endtime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + _logger.warn("Interrupted: " + e); + if (isClosed()) + { + return null; + } + } + } + } + else + { + while (o == null) + { + try + { + o = _synchronousQueue.take(); + } + catch (InterruptedException e) + { + _logger.warn("Interrupted: " + e); + if (isClosed()) + { + return null; + } + } + } + } + final AbstractJMSMessage m = returnMessageOrThrow(o); + if (m != null) + { + preApplicationProcessing(m); + postDeliver(m); + } + return m; + } + finally + { + releaseReceiving(); + } + } + + private boolean closeOnAutoClose() throws JMSException + { + if (isAutoClose() && _closeWhenNoMessages && _synchronousQueue.isEmpty()) + { + close(false); + + return true; + } + else + { + return false; + } + } + + public Message receiveNoWait() throws JMSException + { + checkPreConditions(); + + acquireReceiving(); + + _session.startDistpatcherIfNecessary(); + + try + { + if (closeOnAutoClose()) + { + return null; + } + + Object o = _synchronousQueue.poll(); + final AbstractJMSMessage m = returnMessageOrThrow(o); + if (m != null) + { + preApplicationProcessing(m); + postDeliver(m); + } + + return m; + } + 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 + * + * @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 + { + return (AbstractJMSMessage) o; + } + } + + public void close() throws JMSException + { + close(true); + } + + public void close(boolean sendClose) throws JMSException + { + // synchronized (_closed) + + if (_logger.isInfoEnabled()) + { + _logger.info("Closing consumer:" + debugIdentity()); + } + + synchronized (_connection.getFailoverMutex()) + { + if (!_closed.getAndSet(true)) + { + if (_logger.isTraceEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) + { + _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); + } + else + { + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); + } + } + + if (sendClose) + { + // TODO: Be aware of possible changes to parameter order as versions change. + final AMQFrame cancelFrame = + BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag + false); // nowait + + try + { + _protocolHandler.syncWrite(cancelFrame, BasicCancelOkBody.class); + + if (_logger.isDebugEnabled()) + { + _logger.debug("CancelOk'd for consumer:" + debugIdentity()); + } + + } + catch (AMQException e) + { + throw new JMSAMQException("Error closing consumer: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("FailoverException interrupted basic cancel.", e); + } + } + else + { + // //fixme this probably is not right + // if (!isNoConsume()) + { // done in BasicCancelOK Handler but not sending one so just deregister. + deregisterConsumer(); + } + } + + if ((_messageListener != null) && _receiving.get()) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Interrupting thread: " + _receivingThread); + } + + _receivingThread.interrupt(); + } + } + } + } + + /** + * 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.isTraceEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) + { + _logger.trace(_consumerTag + " markClosed():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); + } + else + { + _closedStack = Arrays.asList(stackTrace).subList(3, stackTrace.length - 1); + } + } + } + + deregisterConsumer(); + } + + /** + * 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 + * @param channelId channel on which this message was sent + */ + void notifyMessage(UnprocessedMessage messageFrame, int channelId) + { + final boolean debug = _logger.isDebugEnabled(); + + if (debug) + { + _logger.debug("notifyMessage called with message number " + messageFrame.getDeliverBody().deliveryTag); + } + + try + { + AbstractJMSMessage jmsMessage = + _messageFactory.createMessage(messageFrame.getDeliverBody().deliveryTag, + messageFrame.getDeliverBody().redelivered, messageFrame.getDeliverBody().exchange, + messageFrame.getDeliverBody().routingKey, messageFrame.getContentHeader(), messageFrame.getBodies()); + + if (debug) + { + _logger.debug("Message is of type: " + jmsMessage.getClass().getName()); + } + // synchronized (_closed) + + { + // if (!_closed.get()) + { + + jmsMessage.setConsumer(this); + + preDeliver(jmsMessage); + + notifyMessage(jmsMessage, channelId); + } + // 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); + } + } + } + + /** + * @param jmsMessage this message has already been processed so can't redo preDeliver + * @param channelId + */ + public void notifyMessage(AbstractJMSMessage jmsMessage, int channelId) + { + try + { + if (isMessageListenerSet()) + { + // we do not need a lock around the test above, and the dispatch below as it is invalid + // for an application to alter an installed listener while the session is started + // synchronized (_closed) + { + // if (!_closed.get()) + { + + preApplicationProcessing(jmsMessage); + getMessageListener().onMessage(jmsMessage); + postDeliver(jmsMessage); + } + } + } + else + { + _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); + } + } + } + + private 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; + } + } + + private void postDeliver(AbstractJMSMessage msg) throws JMSException + { + msg.setJMSDestination(_destination); + switch (_acknowledgeMode) + { + + case Session.CLIENT_ACKNOWLEDGE: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + + break; + + case Session.DUPS_OK_ACKNOWLEDGE: + if (++_outstanding >= _prefetchHigh) + { + _dups_ok_acknowledge_send = true; + } + + if (_outstanding <= _prefetchLow) + { + _dups_ok_acknowledge_send = false; + } + + if (_dups_ok_acknowledge_send) + { + if (!_session.isInRecovery()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), true); + } + } + + break; + + 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. */ + void acknowledgeLastDelivered() + { + if (!_receivedDeliveryTags.isEmpty()) + { + long lastDeliveryTag = _receivedDeliveryTags.poll(); + + while (!_receivedDeliveryTags.isEmpty()) + { + lastDeliveryTag = _receivedDeliveryTags.poll(); + } + + assert _receivedDeliveryTags.isEmpty(); + + _session.acknowledgeMessage(lastDeliveryTag, true); + } + } + + void notifyError(Throwable cause) + { + // synchronized (_closed) + { + _closed.set(true); + if (_logger.isTraceEnabled()) + { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (_closedStack != null) + { + _logger.trace(_consumerTag + " notifyError():" + + Arrays.asList(stackTrace).subList(3, stackTrace.length - 1)); + _logger.trace(_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 AMQShortString getConsumerTag() + { + return _consumerTag; + } + + public void setConsumerTag(AMQShortString 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 void acknowledge() // throws JMSException + { + if (!isClosed()) + { + + Iterator tags = _unacknowledgedDeliveryTags.iterator(); + while (tags.hasNext()) + { + _session.acknowledgeMessage(tags.next(), false); + tags.remove(); + } + } + else + { + throw new IllegalStateException("Consumer is closed"); + } + } + + /** Called on recovery to reset the list of delivery tags */ + public void clearUnackedMessages() + { + _unacknowledgedDeliveryTags.clear(); + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public boolean isNoConsume() + { + return _noConsume; + } + + public void closeWhenNoMessages(boolean b) + { + _closeWhenNoMessages = b; + + if (_closeWhenNoMessages && _synchronousQueue.isEmpty() && _receiving.get() && (_messageListener != null)) + { + _closed.set(true); + _receivingThread.interrupt(); + } + + } + + public void rollback() + { + clearUnackedMessages(); + + if (!_receivedDeliveryTags.isEmpty()) + { + _logger.debug("Rejecting received messages in _receivedDTs (RQ)"); + } + + // rollback received but not committed messages + while (!_receivedDeliveryTags.isEmpty()) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting the messages(" + _receivedDeliveryTags.size() + ") in _receivedDTs (RQ)" + + "for consumer with tag:" + _consumerTag); + } + + Long tag = _receivedDeliveryTags.poll(); + + if (tag != null) + { + if (_logger.isTraceEnabled()) + { + _logger.trace("Rejecting tag from _receivedDTs:" + tag); + } + + _session.rejectMessage(tag, true); + } + } + + if (!_receivedDeliveryTags.isEmpty()) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Queue _receivedDTs (RQ) was not empty after rejection"); + } + } + + // rollback pending messages + 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.isTraceEnabled()) + { + _logger.trace("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(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java new file mode 100644 index 0000000000..0ee4882ec2 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -0,0 +1,691 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.mina.common.ByteBuffer; + +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.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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.Queue; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +public 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. + */ + private 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; + + private AMQProtocolHandler _protocolHandler; + + /** + * True if this producer was created from a transacted session + */ + private boolean _transacted; + + private 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 + */ + private AMQSession _session; + + private final boolean _immediate; + + private final boolean _mandatory; + + private final boolean _waitUntilSent; + + private boolean _disableMessageId; + + 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) + { + declareDestination(destination); + } + + _immediate = immediate; + _mandatory = mandatory; + _waitUntilSent = waitUntilSent; + } + + void resubscribe() throws AMQException + { + if (_destination != null) + { + declareDestination(_destination); + } + } + + private void declareDestination(AMQDestination destination) + { + // Declare the exchange + // Note that the durable and internal arguments are ignored since passive is set to false + // TODO: Be aware of possible changes to parameter order as versions change. + AMQFrame declare = + ExchangeDeclareBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), null, // arguments + false, // autoDelete + false, // durable + destination.getExchangeName(), // exchange + false, // internal + true, // nowait + false, // passive + _session.getTicket(), // ticket + destination.getExchangeClass()); // type + _protocolHandler.writeFrame(declare); + } + + 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((BytesMessage) message).getConvertedMessage(); + } + else if (message instanceof MapMessage) + { + newMessage = new MessageConverter((MapMessage) message).getConvertedMessage(); + } + else if (message instanceof ObjectMessage) + { + newMessage = new MessageConverter((ObjectMessage) message).getConvertedMessage(); + } + else if (message instanceof TextMessage) + { + newMessage = new MessageConverter((TextMessage) message).getConvertedMessage(); + } + else if (message instanceof StreamMessage) + { + newMessage = new MessageConverter((StreamMessage) message).getConvertedMessage(); + } + else + { + newMessage = new MessageConverter(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)); + } + + declareDestination((AMQDestination) destination); + } + + 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 (_disableMessageId) + { + message.setJMSMessageID(null); + } + else + { + if (message.getJMSMessageID() == null) + { + message.setJMSMessageID(UUID.randomUUID().toString()); + } + } + + int type; + if (destination instanceof Topic) + { + type = AMQDestination.TOPIC_TYPE; + } + else if (destination instanceof Queue) + { + type = AMQDestination.QUEUE_TYPE; + } + else + { + type = AMQDestination.UNKNOWN_TYPE; + } + + message.getJmsHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type); + + // 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. + AMQFrame publishFrame = + BasicPublishBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), destination.getExchangeName(), // exchange + immediate, // immediate + mandatory, // mandatory + destination.getRoutingKey(), // routingKey + _session.getTicket()); // ticket + + message.prepareForSending(); + ByteBuffer payload = message.getData(); + BasicContentHeaderProperties contentHeaderProperties = message.getContentHeaderProperties(); + + 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); + } + + // weight argument of zero indicates no child content headers, just bodies + // 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. + AMQFrame contentHeaderFrame = + ContentHeaderBody.createAMQFrame(_channelId, + BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion()), 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); + _protocolHandler.writeFrame(compositeFrame, 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()); + } + } + + 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"); + } + } + } + + /** + * 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; + } + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/Closeable.java b/Final2/java/client/src/main/java/org/apache/qpid/client/Closeable.java new file mode 100644 index 0000000000..7e119343a1 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java b/Final2/java/client/src/main/java/org/apache/qpid/client/ConnectionTuneParameters.java new file mode 100644 index 0000000000..b1ec7216bc --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java b/Final2/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.java new file mode 100644 index 0000000000..a5e89ef4fc --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/CustomJMSXProperty.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 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; + + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java b/Final2/java/client/src/main/java/org/apache/qpid/client/DispatcherCallback.java new file mode 100644 index 0000000000..81a55006ed --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java new file mode 100644 index 0000000000..0927ca3625 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/JmsNotImplementedException.java new file mode 100644 index 0000000000..903514c35f --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java b/Final2/java/client/src/main/java/org/apache/qpid/client/MessageConsumerPair.java new file mode 100644 index 0000000000..585d6db3fd --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java b/Final2/java/client/src/main/java/org/apache/qpid/client/QpidConnectionMetaData.java new file mode 100644 index 0000000000..3bb5707417 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java b/Final2/java/client/src/main/java/org/apache/qpid/client/QueueReceiverAdaptor.java new file mode 100644 index 0000000000..7059588367 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java new file mode 100644 index 0000000000..493e2b5ec0 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.MessageProducer; +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.isValidated() && checkQueueBeforePublish()) + { + + if (_delegate.getSession().isStrictAMQP()) + { + _delegate._logger.warn("AMQP does not support destination validation before publish, "); + destination.setValidated(true); + } + else + { + if (_delegate.isBound(destination)) + { + destination.setValidated(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/Final2/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java b/Final2/java/client/src/main/java/org/apache/qpid/client/SSLConfiguration.java new file mode 100644 index 0000000000..2280cc9870 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java b/Final2/java/client/src/main/java/org/apache/qpid/client/TemporaryDestination.java new file mode 100644 index 0000000000..03d25aa243 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/TopicPublisherAdapter.java new file mode 100644 index 0000000000..81b9940ed5 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java b/Final2/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java new file mode 100644 index 0000000000..d4bec5d906 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/TopicSubscriberAdaptor.java @@ -0,0 +1,126 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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 BasicMessageConsumer _consumer; + private final boolean _noLocal; + + TopicSubscriberAdaptor(Topic topic, BasicMessageConsumer consumer, boolean noLocal) + { + _topic = topic; + _consumer = consumer; + _noLocal = noLocal; + } + + TopicSubscriberAdaptor(Topic topic, BasicMessageConsumer 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{ + BasicMessageConsumer msgConsumer = (BasicMessageConsumer)_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"); + } + } + + BasicMessageConsumer getMessageConsumer() + { + return _consumer; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java new file mode 100644 index 0000000000..037b0dc2d1 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java new file mode 100644 index 0000000000..5303993331 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.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.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.propagateExceptionToWaiters(new FailoverException("Failing over about to start")); + + // 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()) + { + // 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(_amqProtocolHandler.getProtocolSession())); + if (!_amqProtocolHandler.getConnection().firePreFailover(_host != null)) + { + _logger.info("Failover process veto-ed by client"); + + _amqProtocolHandler.setStateManager(existingStateManager); + if (_host != null) + { + _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException( + "Redirect was vetoed by client")); + } + else + { + _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException( + "Failover was vetoed by client")); + } + + _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) + { + _amqProtocolHandler.setStateManager(existingStateManager); + _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException( + "Server closed connection and no failover " + "was successful")); + } + else + { + _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/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java new file mode 100644 index 0000000000..1ec98efe0e --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java new file mode 100644 index 0000000000..9a7f43926e --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java new file mode 100644 index 0000000000..120a07f0fc --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.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 + { + while (true) + { + try + { + connection.blockUntilNotFailingOver(); + } + catch (InterruptedException e) + { + _log.debug("Interrupted: " + e, e); + + return null; + } + + synchronized (connection.getFailoverMutex()) + { + try + { + return operation.execute(); + } + catch (FailoverException e) + { + _log.debug("Failover exception caught during operation: " + e, e); + } + } + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java new file mode 100644 index 0000000000..807a5f7d13 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java b/Final2/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java new file mode 100644 index 0000000000..ef2e7e1d65 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java new file mode 100644 index 0000000000..8f0ee05b3e --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + BasicCancelOkBody body = (BasicCancelOkBody) evt.getMethod(); + + if (_logger.isInfoEnabled()) + { + _logger.info("New BasicCancelOk method received for consumer:" + body.consumerTag); + } + + protocolSession.confirmConsumerCancelled(evt.getChannelId(), body.consumerTag); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java new file mode 100644 index 0000000000..51120da55c --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.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.message.UnprocessedMessage; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicDeliverBody) evt.getMethod()); + _logger.debug("New JmsDeliver method received"); + protocolSession.unprocessedMessageReceived(msg); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java new file mode 100644 index 0000000000..0f00c6a26e --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.BasicReturnBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.debug("New JmsBounce method received"); + final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicReturnBody) evt.getMethod()); + + protocolSession.unprocessedMessageReceived(msg); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java new file mode 100644 index 0000000000..139a32370e --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.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.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.AMQStateManager; +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.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.debug("ChannelClose method received"); + ChannelCloseBody method = (ChannelCloseBody) evt.getMethod(); + + AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); + AMQShortString reason = method.replyText; + if (_logger.isDebugEnabled()) + { + _logger.debug("Channel close reply code: " + errorCode + ", reason: " + reason); + } + + // 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) + { + 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); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new org.apache.qpid.AMQInvalidArgumentException(String.valueOf(reason)); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason)); + } + else + { + throw new AMQChannelClosedException(errorCode, "Error: " + reason); + } + + } + // fixme why is this only done when the close is expected... + // should the above forced closes not also cause a close? + protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason)); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java new file mode 100644 index 0000000000..e1fe2697e5 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId()); + + // todo this should do the local closure + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java new file mode 100644 index 0000000000..ca3f46d08b --- /dev/null +++ b/Final2/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.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ChannelFlowOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod(); + _logger.debug("Received Channel.Flow-Ok message, active = " + method.active); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java new file mode 100644 index 0000000000..df096d3c4e --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.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.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.AMQStateManager; +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.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.info("ConnectionClose frame received"); + ConnectionCloseBody method = (ConnectionCloseBody) evt.getMethod(); + + // does it matter + // stateManager.changeState(AMQState.CONNECTION_CLOSING); + + AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); + AMQShortString reason = method.replyText; + + try + { + // TODO: check whether channel id of zero is appropriate + // Be aware of possible changes to parameter order as versions change. + protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(), + method.getMinor())); + + if (errorCode != AMQConstant.REPLY_SUCCESS) + { + if (errorCode == AMQConstant.NOT_ALLOWED) + { + _logger.info("Authentication Error:" + Thread.currentThread().getName()); + + protocolSession.closeProtocolSession(); + + // todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state. + stateManager.changeState(AMQState.CONNECTION_NOT_STARTED); + + throw new AMQAuthenticationException(errorCode, (reason == null) ? null : reason.toString()); + } + else + { + _logger.info("Connection close received with error code " + errorCode); + + throw new AMQConnectionClosedException(errorCode, "Error: " + reason); + } + } + } + finally + { + // this actually closes the connection in the case where it is not an error. + + protocolSession.closeProtocolSession(); + + stateManager.changeState(AMQState.CONNECTION_CLOSED); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.java new file mode 100644 index 0000000000..2e0f273c32 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionOpenOkMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.protocol.AMQMethodEvent; + +public class ConnectionOpenOkMethodHandler implements StateAwareMethodListener +{ + private static final ConnectionOpenOkMethodHandler _instance = new ConnectionOpenOkMethodHandler(); + + public static ConnectionOpenOkMethodHandler getInstance() + { + return _instance; + } + + private ConnectionOpenOkMethodHandler() + { + } + + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + { + stateManager.changeState(AMQState.CONNECTION_OPEN); + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java new file mode 100644 index 0000000000..213c0eba6e --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.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.client.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ConnectionRedirectBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.info("ConnectionRedirect frame received"); + ConnectionRedirectBody method = (ConnectionRedirectBody) evt.getMethod(); + + String host = method.host.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); + + } + + protocolSession.failover(host, port); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.java new file mode 100644 index 0000000000..ab6acffeaf --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionSecureMethodHandler.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.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.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionSecureOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +public class ConnectionSecureMethodHandler implements StateAwareMethodListener +{ + private static final ConnectionSecureMethodHandler _instance = new ConnectionSecureMethodHandler(); + + public static ConnectionSecureMethodHandler getInstance() + { + return _instance; + } + + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + { + SaslClient client = protocolSession.getSaslClient(); + if (client == null) + { + throw new AMQException("No SASL client set up - cannot proceed with authentication"); + } + + ConnectionSecureBody body = (ConnectionSecureBody) evt.getMethod(); + + try + { + // Evaluate server challenge + byte[] response = client.evaluateChallenge(body.challenge); + // 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. + AMQFrame responseFrame = ConnectionSecureOkBody.createAMQFrame(evt.getChannelId(), + body.getMajor(), body.getMinor(), + response); // response + protocolSession.writeFrame(responseFrame); + } + catch (SaslException e) + { + throw new AMQException("Error processing SASL challenge: " + e, e); + } + + + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java new file mode 100644 index 0000000000..f14e256172 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.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.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.AMQStateManager; +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.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, " + + "AMQMethodEvent evt): called"); + + ConnectionStartBody body = (ConnectionStartBody) evt.getMethod(); + + ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor, (byte) body.versionMinor); + + // 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()) + { + protocolSession.setProtocolVersion(pv.getMajorVersion(), pv.getMinorVersion()); + + try + { + // Used to hold the SASL mechanism to authenticate with. + String mechanism; + + if (body.mechanisms == null) + { + throw new AMQException("mechanism not specified in ConnectionStart method frame"); + } + else + { + mechanism = chooseMechanism(body.mechanisms); + _log.debug("mechanism = " + mechanism); + } + + if (mechanism == null) + { + throw new AMQException("No supported security mechanism found, passed: " + new String(body.mechanisms)); + } + + byte[] saslResponse; + try + { + SaslClient sc = + Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null, + createCallbackHandler(mechanism, protocolSession)); + if (sc == null) + { + throw new AMQException( + "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."); + } + + protocolSession.setSaslClient(sc); + saslResponse = (sc.hasInitialResponse() ? sc.evaluateChallenge(new byte[0]) : null); + } + catch (SaslException e) + { + protocolSession.setSaslClient(null); + throw new AMQException("Unable to create SASL client: " + e, e); + } + + if (body.locales == null) + { + throw new AMQException("Locales is not defined in Connection Start method"); + } + + final String locales = new String(body.locales, "utf8"); + final StringTokenizer tokenizer = new StringTokenizer(locales, " "); + String selectedLocale = null; + if (tokenizer.hasMoreTokens()) + { + selectedLocale = tokenizer.nextToken(); + } + else + { + throw new AMQException("No locales sent from server, passed: " + locales); + } + + stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); + FieldTable clientProperties = FieldTableFactory.newFieldTable(); + + clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()), + protocolSession.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()); + + // 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. + protocolSession.writeFrame(ConnectionStartOkBody.createAMQFrame(evt.getChannelId(), + protocolSession.getProtocolMajorVersion(), protocolSession.getProtocolMinorVersion(), + clientProperties, // clientProperties + new AMQShortString(selectedLocale), // locale + new AMQShortString(mechanism), // mechanism + saslResponse)); // response + + } + catch (UnsupportedEncodingException e) + { + throw new AMQException("Unable to decode data: " + e, e); + } + } + else + { + _log.error("Broker requested Protocol [" + body.versionMajor + "-" + body.versionMinor + + "] which is not supported by this version of the client library"); + + protocolSession.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("Unable to create callback handler: " + e, e); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java new file mode 100644 index 0000000000..68556991d7 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.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.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.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionOpenBody; +import org.apache.qpid.framing.ConnectionTuneBody; +import org.apache.qpid.framing.ConnectionTuneOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.debug("ConnectionTune frame received"); + ConnectionTuneBody frame = (ConnectionTuneBody) evt.getMethod(); + + ConnectionTuneParameters params = protocolSession.getConnectionTuneParameters(); + if (params == null) + { + params = new ConnectionTuneParameters(); + } + + params.setFrameMax(frame.frameMax); + params.setChannelMax(frame.channelMax); + params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.heartbeat)); + protocolSession.setConnectionTuneParameters(params); + + stateManager.changeState(AMQState.CONNECTION_NOT_OPENED); + protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params, frame.getMajor(), frame.getMinor())); + + String host = protocolSession.getAMQConnection().getVirtualHost(); + AMQShortString virtualHost = new AMQShortString("/" + host); + + protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true, frame.getMajor(), + frame.getMinor())); + } + + protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities, + boolean insist, byte major, byte minor) + { + // Be aware of possible changes to parameter order as versions change. + return ConnectionOpenBody.createAMQFrame(channel, major, minor, // AMQP version (major, minor) + capabilities, // capabilities + insist, // insist + path); // virtualHost + } + + protected AMQFrame createTuneOkFrame(int channel, ConnectionTuneParameters params, byte major, byte minor) + { + // Be aware of possible changes to parameter order as versions change. + return ConnectionTuneOkBody.createAMQFrame(channel, major, minor, params.getChannelMax(), // channelMax + params.getFrameMax(), // frameMax + params.getHeartbeat()); // heartbeat + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java new file mode 100644 index 0000000000..862a9be8d4 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.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.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.ExchangeBoundOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + if (_logger.isDebugEnabled()) + { + ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod(); + _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: " + + body.replyText); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java new file mode 100644 index 0000000000..65060d44d2 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.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.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + if (_logger.isDebugEnabled()) + { + QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod(); + _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.java new file mode 100644 index 0000000000..8741a1cbb6 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AMQMessage.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.client.message; + +import javax.jms.JMSException; + +import org.apache.qpid.client.AMQSession; +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 java.math.BigDecimal; + +public class AMQMessage +{ + protected ContentHeaderProperties _contentHeaderProperties; + + /** If the acknowledge mode is CLIENT_ACKNOWLEDGE the session is required */ + protected AMQSession _session; + + protected final long _deliveryTag; + + public AMQMessage(ContentHeaderProperties properties, long deliveryTag) + { + _contentHeaderProperties = properties; + _deliveryTag = deliveryTag; + } + + public AMQMessage(ContentHeaderProperties properties) + { + this(properties, -1); + } + + /** + * 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; + } + + /** Invoked prior to sending the message. Allows the message to be modified if necessary before sending. */ + public void prepareForSending() throws JMSException + { + } + + public FieldTable getPropertyHeaders() + { + return ((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders(); + } + + public void setDecimalProperty(AMQShortString propertyName, BigDecimal bd) throws JMSException + { + getPropertyHeaders().setDecimal(propertyName, bd); + } + + public void setIntProperty(AMQShortString propertyName, int i) throws JMSException + { + getPropertyHeaders().setInteger(propertyName, new Integer(i)); + } + + public void setLongStringProperty(AMQShortString propertyName, String value) + { + getPropertyHeaders().setString(propertyName, value); + } + + public void setTimestampProperty(AMQShortString propertyName, long value) + { + getPropertyHeaders().setTimestamp(propertyName, value); + } + + public void setVoidProperty(AMQShortString propertyName) + { + getPropertyHeaders().setVoid(propertyName); + } + + //** Getters + + public BigDecimal getDecimalProperty(AMQShortString propertyName) throws JMSException + { + return getPropertyHeaders().getDecimal(propertyName); + } + + public int getIntegerProperty(AMQShortString propertyName) throws JMSException + { + return getPropertyHeaders().getInteger(propertyName); + } + + public String getLongStringProperty(AMQShortString propertyName) + { + return getPropertyHeaders().getString(propertyName); + } + + public Long getTimestampProperty(AMQShortString propertyName) + { + return getPropertyHeaders().getTimestamp(propertyName); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.java new file mode 100644 index 0000000000..af254fbbaf --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesMessage.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.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; +import org.apache.qpid.framing.ContentHeaderBody; + +/** + * @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() + { + this(null); + } + + /** + * Construct a bytes message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + AbstractBytesMessage(ByteBuffer data) + { + super(data); // this instanties a content header + getContentHeaderProperties().setContentType(getMimeTypeAsShortString()); + + if (_data == null) + { + allocateInitialBuffer(); + } + } + + protected void allocateInitialBuffer() + { + _data = ByteBuffer.allocate(DEFAULT_BUFFER_INITIAL_SIZE); + _data.setAutoExpand(true); + } + + AbstractBytesMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + // TODO: this casting is ugly. Need to review whole ContentHeaderBody idea + super(messageNbr, (BasicContentHeaderProperties) contentHeader.properties, exchange, routingKey, data); + getContentHeaderProperties().setContentType(getMimeTypeAsShortString()); + } + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java new file mode 100644 index 0000000000..3b8ce9a98a --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractBytesTypedMessage.java @@ -0,0 +1,801 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.ContentHeaderBody; + +/** + * @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() + { + this(null); + } + + /** + * Construct a stream message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + AbstractBytesTypedMessage(ByteBuffer data) + { + super(data); // this instanties a content header + } + + + AbstractBytesTypedMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java new file mode 100644 index 0000000000..2dfeb19268 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -0,0 +1,685 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQException; +import org.apache.qpid.client.*; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; +import org.apache.qpid.url.URLSyntaxException; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.UUID; + +public abstract class AbstractJMSMessage extends AMQMessage implements org.apache.qpid.jms.Message +{ + private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); + + protected boolean _redelivered; + + protected ByteBuffer _data; + private boolean _readableProperties = false; + protected boolean _readableMessage = false; + protected boolean _changedData; + private Destination _destination; + private JMSHeaderAdapter _headerAdapter; + private BasicMessageConsumer _consumer; + private boolean _strictAMQP; + + protected AbstractJMSMessage(ByteBuffer data) + { + super(new BasicContentHeaderProperties()); + _data = data; + if (_data != null) + { + _data.acquire(); + } + + _readableProperties = false; + _readableMessage = (data != null); + _changedData = (data == null); + _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); + + _strictAMQP = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); + } + + protected AbstractJMSMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + this(contentHeader, deliveryTag); + + Integer type = contentHeader.getHeaders().getInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName()); + + AMQDestination dest; + + if (AMQDestination.QUEUE_TYPE.equals(type)) + { + dest = new AMQQueue(exchange, routingKey, routingKey); + } + else if (AMQDestination.TOPIC_TYPE.equals(type)) + { + dest = new AMQTopic(exchange, routingKey, null); + } + else + { + dest = new AMQUndefinedDestination(exchange, routingKey, null); + } + // Destination dest = AMQDestination.createDestination(url); + setJMSDestination(dest); + + _data = data; + if (_data != null) + { + _data.acquire(); + } + + _readableMessage = data != null; + + } + + protected AbstractJMSMessage(BasicContentHeaderProperties contentHeader, long deliveryTag) + { + super(contentHeader, deliveryTag); + _readableProperties = (_contentHeaderProperties != null); + _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); + } + + public String getJMSMessageID() throws JMSException + { + if (getContentHeaderProperties().getMessageIdAsString() == null) + { + getContentHeaderProperties().setMessageId("ID:" + UUID.randomUUID()); + } + + return getContentHeaderProperties().getMessageIdAsString(); + } + + public void setJMSMessageID(String messageId) throws JMSException + { + getContentHeaderProperties().setMessageId(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 (URLSyntaxException 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 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 boolean getJMSRedelivered() throws JMSException + { + return _redelivered; + } + + public void setJMSRedelivered(boolean b) throws JMSException + { + _redelivered = b; + } + + 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 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 clearBody() throws JMSException + { + clearBodyImpl(); + _readableMessage = false; + } + + public boolean propertyExists(AMQShortString propertyName) throws JMSException + { + return getJmsHeaders().propertyExists(propertyName); + } + + public boolean propertyExists(String propertyName) throws JMSException + { + return getJmsHeaders().propertyExists(propertyName); + } + + public boolean getBooleanProperty(AMQShortString propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getBoolean(propertyName); + } + + public boolean getBooleanProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getBoolean(propertyName); + } + + public byte getByteProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getByte(propertyName); + } + + public byte[] getBytesProperty(AMQShortString propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getBytes(propertyName); + } + + public short getShortProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getShort(propertyName); + } + + public int getIntProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getInteger(propertyName); + } + + public long getLongProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getLong(propertyName); + } + + public float getFloatProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getFloat(propertyName); + } + + public double getDoubleProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + return getJmsHeaders().getDouble(propertyName); + } + + public String getStringProperty(String propertyName) throws JMSException + { + if (_strictAMQP) + { + 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(AMQShortString propertyName, boolean b) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setBoolean(propertyName, b); + } + + public void setBooleanProperty(String propertyName, boolean b) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setBoolean(propertyName, b); + } + + public void setByteProperty(String propertyName, byte b) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setByte(propertyName, new Byte(b)); + } + + public void setBytesProperty(AMQShortString propertyName, byte[] bytes) throws JMSException + { + if (_strictAMQP) + { + throw new UnsupportedOperationException("JMS Proprerties not supported in AMQP"); + } + + checkWritableProperties(); + getJmsHeaders().setBytes(propertyName, bytes); + } + + public void setShortProperty(String propertyName, short i) throws JMSException + { + if (_strictAMQP) + { + 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(); + JMSHeaderAdapter.checkPropertyName(propertyName); + super.setIntProperty(new AMQShortString(propertyName), new Integer(i)); + } + + public void setLongProperty(String propertyName, long l) throws JMSException + { + if (_strictAMQP) + { + 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 (_strictAMQP) + { + 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 (_strictAMQP) + { + 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(); + JMSHeaderAdapter.checkPropertyName(propertyName); + super.setLongStringProperty(new AMQShortString(propertyName), value); + } + + public void setObjectProperty(String propertyName, Object object) throws JMSException + { + checkWritableProperties(); + getJmsHeaders().setObject(propertyName, object); + } + + protected void removeProperty(AMQShortString propertyName) throws JMSException + { + getJmsHeaders().remove(propertyName); + } + + protected void removeProperty(String propertyName) throws JMSException + { + getJmsHeaders().remove(propertyName); + } + + 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(); + } + } + + /** + * 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; + + public String getMimeType() + { + return getMimeTypeAsShortString().toString(); + } + + public abstract AMQShortString getMimeTypeAsShortString(); + + 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(_deliveryTag); + + buf.append("\nProperties:"); + if (getJmsHeaders().isEmpty()) + { + buf.append(""); + } + else + { + buf.append('\n').append(getJmsHeaders().getHeaders()); + } + + return buf.toString(); + } + catch (JMSException e) + { + return e.toString(); + } + } + + public void setUnderlyingMessagePropertiesMap(FieldTable messageProperties) + { + getContentHeaderProperties().setHeaders(messageProperties); + } + + public JMSHeaderAdapter getJmsHeaders() + { + return _headerAdapter; + } + + 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"); + } + } + + protected void checkWritableProperties() throws MessageNotWriteableException + { + if (_readableProperties) + { + throw new MessageNotWriteableException("You need to call clearProperties() to make the message writable"); + } + } + + public boolean isReadable() + { + return _readableMessage; + } + + public boolean isWritable() + { + return !_readableMessage; + } + + public void reset() + { + if (!_changedData) + { + _data.rewind(); + } + else + { + _data.flip(); + _changedData = false; + } + } + + public void setConsumer(BasicMessageConsumer basicMessageConsumer) + { + _consumer = basicMessageConsumer; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java new file mode 100644 index 0000000000..87df7e1337 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.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.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.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 abstract AbstractJMSMessage createMessage(long messageNbr, ByteBuffer data, AMQShortString exchange, + AMQShortString routingKey, ContentHeaderBody contentHeader) throws AMQException; + + protected AbstractJMSMessage createMessageWithBody(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(); + data.put(cb.payload); + cb.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()); + } + + return createMessage(messageNbr, data, exchange, routingKey, contentHeader); + } + + public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, List bodies) throws JMSException, AMQException + { + final AbstractJMSMessage msg = createMessageWithBody(messageNbr, contentHeader, exchange, routingKey, bodies); + msg.setJMSRedelivered(redelivered); + + return msg; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.java new file mode 100644 index 0000000000..19382b58c3 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessage.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.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.ContentHeaderBody; + +public class JMSBytesMessage extends AbstractBytesMessage implements BytesMessage +{ + public static final String MIME_TYPE = "application/octet-stream"; + private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); + + + public JMSBytesMessage() + { + this(null); + } + + /** + * Construct a bytes message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + JMSBytesMessage(ByteBuffer data) + { + super(data); // this instanties a content header + } + + JMSBytesMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, data); + } + + public void reset() + { + super.reset(); + _readableMessage = true; + } + + public AMQShortString getMimeTypeAsShortString() + { + return MIME_TYPE_SHORT_STRING; + } + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.java new file mode 100644 index 0000000000..fd2aae9feb --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSBytesMessageFactory.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 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.ContentHeaderBody; + +public class JMSBytesMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data, + AMQShortString exchange, AMQShortString routingKey, + ContentHeaderBody contentHeader) throws AMQException + { + return new JMSBytesMessage(deliveryTag, contentHeader, exchange, routingKey, data); + } + + public AbstractJMSMessage createMessage() throws JMSException + { + return new JMSBytesMessage(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSHeaderAdapter.java new file mode 100644 index 0000000000..39b4e1e27b --- /dev/null +++ b/Final2/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 static 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 static 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java new file mode 100644 index 0000000000..a70acbabbe --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.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.message; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; + +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 static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); + + private Map _map = new HashMap(); + + public JMSMapMessage() throws JMSException + { + this(null); + } + + JMSMapMessage(ByteBuffer data) throws JMSException + { + super(data); // this instantiates a content header + populateMapFromData(); + } + + JMSMapMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey, + ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, data); + try + { + populateMapFromData(); + } + catch (JMSException je) + { + throw new AMQException("Error populating MapMessage from ByteBuffer", je); + + } + + } + + public String toBodyString() throws JMSException + { + return _map.toString(); + } + + public AMQShortString getMimeTypeAsShortString() + { + return MIME_TYPE_SHORT_STRING; + } + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.java new file mode 100644 index 0000000000..a6b9bb29a4 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessageFactory.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 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.ContentHeaderBody; + +public class JMSMapMessageFactory extends AbstractJMSMessageFactory +{ + public AbstractJMSMessage createMessage() throws JMSException + { + return new JMSMapMessage(); + } + + protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data, + AMQShortString exchange, AMQShortString routingKey, + ContentHeaderBody contentHeader) throws AMQException + { + return new JMSMapMessage(deliveryTag, contentHeader, exchange, routingKey, data); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.java new file mode 100644 index 0000000000..caf8741280 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessage.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.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; +import org.apache.qpid.framing.ContentHeaderBody; + +public class JMSObjectMessage extends AbstractJMSMessage implements ObjectMessage +{ + public static final String MIME_TYPE = "application/java-object-stream"; + private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); + + private static final int DEFAULT_BUFFER_SIZE = 1024; + + /** + * Creates empty, writable message for use by producers + */ + public JMSObjectMessage() + { + this(null); + } + + private JMSObjectMessage(ByteBuffer data) + { + super(data); + if (data == null) + { + _data = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE); + _data.setAutoExpand(true); + } + + getContentHeaderProperties().setContentType(MIME_TYPE_SHORT_STRING); + } + + /** + * Creates read only message for delivery to consumers + */ + JMSObjectMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey, + ByteBuffer data) throws AMQException + { + super(messageNbr, (BasicContentHeaderProperties) contentHeader.properties, exchange, routingKey, data); + } + + public void clearBodyImpl() throws JMSException + { + if (_data != null) + { + _data.release(); + } + + _data = null; + + } + + public String toBodyString() throws JMSException + { + return toString(_data); + } + + public AMQShortString getMimeTypeAsShortString() + { + return MIME_TYPE_SHORT_STRING; + } + + 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) + { } + } + + private static String toString(ByteBuffer data) + { + if (data == null) + { + return null; + } + + int pos = data.position(); + try + { + return data.getString(Charset.forName("UTF8").newDecoder()); + } + catch (CharacterCodingException e) + { + return null; + } + finally + { + data.position(pos); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.java new file mode 100644 index 0000000000..57ac4fb006 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSObjectMessageFactory.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 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.ContentHeaderBody; + +public class JMSObjectMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data, + AMQShortString exchange, AMQShortString routingKey, + ContentHeaderBody contentHeader) throws AMQException + { + return new JMSObjectMessage(deliveryTag, contentHeader, exchange, routingKey, data); + } + + public AbstractJMSMessage createMessage() throws JMSException + { + return new JMSObjectMessage(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.java new file mode 100644 index 0000000000..b4350c7a98 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessage.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.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.ContentHeaderBody; + +/** + * @author Apache Software Foundation + */ +public class JMSStreamMessage extends AbstractBytesTypedMessage implements StreamMessage +{ + public static final String MIME_TYPE="jms/stream-message"; + private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); + + + /** + * 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() + { + this(null); + } + + /** + * Construct a stream message with existing data. + * + * @param data the data that comprises this message. If data is null, you get a 1024 byte buffer that is + * set to auto expand + */ + JMSStreamMessage(ByteBuffer data) + { + super(data); // this instanties a content header + } + + + JMSStreamMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) throws AMQException + { + super(messageNbr, contentHeader, exchange, routingKey, data); + } + + public void reset() + { + super.reset(); + _readableMessage = true; + } + + public AMQShortString getMimeTypeAsShortString() + { + return MIME_TYPE_SHORT_STRING; + } + + + + 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/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.java new file mode 100644 index 0000000000..c34ee7175d --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSStreamMessageFactory.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 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.ContentHeaderBody; + +public class JMSStreamMessageFactory extends AbstractJMSMessageFactory +{ + protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data, + AMQShortString exchange, AMQShortString routingKey, + ContentHeaderBody contentHeader) throws AMQException + { + return new JMSStreamMessage(deliveryTag, contentHeader, exchange, routingKey, data); + } + + public AbstractJMSMessage createMessage() throws JMSException + { + return new JMSStreamMessage(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.java new file mode 100644 index 0000000000..87cc80f21d --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessage.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.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; + +public class JMSTextMessage extends AbstractJMSMessage implements javax.jms.TextMessage +{ + private static final String MIME_TYPE = "text/plain"; + private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); + + + private String _decodedValue; + + /** + * This constant represents the name of a property that is set when the message payload is null. + */ + private static final AMQShortString PAYLOAD_NULL_PROPERTY = CustomJMSXProperty.JMS_AMQP_NULL.getShortStringName(); + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + public JMSTextMessage() throws JMSException + { + this(null, null); + } + + JMSTextMessage(ByteBuffer data, String encoding) throws JMSException + { + super(data); // this instantiates a content header + getContentHeaderProperties().setContentType(MIME_TYPE_SHORT_STRING); + getContentHeaderProperties().setEncoding(encoding); + } + + JMSTextMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, + AMQShortString routingKey, ByteBuffer data) + throws AMQException + { + super(deliveryTag, contentHeader, exchange, routingKey, data); + contentHeader.setContentType(MIME_TYPE_SHORT_STRING); + _data = data; + } + + JMSTextMessage(ByteBuffer data) throws JMSException + { + this(data, null); + } + + JMSTextMessage(String text) throws JMSException + { + super((ByteBuffer) null); + setText(text); + } + + public void clearBodyImpl() throws JMSException + { + if (_data != null) + { + _data.release(); + } + _data = null; + _decodedValue = null; + } + + public String toBodyString() throws JMSException + { + return getText(); + } + + public void setData(ByteBuffer data) + { + _data = data; + } + + public AMQShortString getMimeTypeAsShortString() + { + return MIME_TYPE_SHORT_STRING; + } + + public void setText(String text) throws JMSException + { + checkWritable(); + + clearBody(); + try + { + if (text != null) + { + _data = ByteBuffer.allocate(text.length()); + _data.limit(text.length()) ; + //_data.sweep(); + _data.setAutoExpand(true); + final String encoding = getContentHeaderProperties().getEncodingAsString(); + if (encoding == null) + { + _data.put(text.getBytes(DEFAULT_CHARSET.name())); + } + else + { + _data.put(text.getBytes(encoding)); + } + _changedData=true; + } + _decodedValue = text; + } + catch (UnsupportedEncodingException e) + { + // should never occur + JMSException jmse = new JMSException("Unable to decode text data"); + jmse.setLinkedException(e); + } + } + + 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 (getContentHeaderProperties().getEncodingAsString() != null) + { + try + { + _decodedValue = _data.getString(Charset.forName(getContentHeaderProperties().getEncodingAsString()).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/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.java new file mode 100644 index 0000000000..c5942dbe2a --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/JMSTextMessageFactory.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; + +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.ContentHeaderBody; + +public class JMSTextMessageFactory extends AbstractJMSMessageFactory +{ + + public AbstractJMSMessage createMessage() throws JMSException + { + return new JMSTextMessage(); + } + + protected AbstractJMSMessage createMessage(long deliveryTag, ByteBuffer data, + AMQShortString exchange, AMQShortString routingKey, + ContentHeaderBody contentHeader) throws AMQException + { + return new JMSTextMessage(deliveryTag, (BasicContentHeaderProperties) contentHeader.properties, + exchange, routingKey, data); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java new file mode 100644 index 0000000000..f6b11c6f6c --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java @@ -0,0 +1,202 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageEOFException; +import javax.jms.ObjectMessage; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; + +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(BytesMessage message) throws JMSException + { + BytesMessage bytesMessage = (BytesMessage) message; + bytesMessage.reset(); + + JMSBytesMessage nativeMsg = new JMSBytesMessage(); + + byte[] buf = new byte[1024]; + + int len; + + while ((len = bytesMessage.readBytes(buf)) != -1) + { + nativeMsg.writeBytes(buf, 0, len); + } + + _newMessage = nativeMsg; + setMessageProperties(message); + } + + public MessageConverter(MapMessage message) throws JMSException + { + MapMessage nativeMessage = new JMSMapMessage(); + + 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(ObjectMessage message) throws JMSException + { + ObjectMessage origMessage = (ObjectMessage) message; + ObjectMessage nativeMessage = new JMSObjectMessage(); + + nativeMessage.setObject(origMessage.getObject()); + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + + } + + public MessageConverter(TextMessage message) throws JMSException + { + TextMessage nativeMessage = new JMSTextMessage(); + + nativeMessage.setText(message.getText()); + + _newMessage = (AbstractJMSMessage) nativeMessage; + setMessageProperties(message); + } + + public MessageConverter(StreamMessage message) throws JMSException + { + StreamMessage nativeMessage = new JMSStreamMessage(); + + 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(Message message) throws JMSException + { + // Send a message with just properties. + // Throwing away content + BytesMessage nativeMessage = new JMSBytesMessage(); + + _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/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.java new file mode 100644 index 0000000000..0fe4af715d --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactory.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 java.util.List; + +import javax.jms.JMSException; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; + + +public interface MessageFactory +{ + AbstractJMSMessage createMessage(long deliveryTag, boolean redelivered, + ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, + List bodies) + throws JMSException, AMQException; + + AbstractJMSMessage createMessage() throws JMSException; +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.java new file mode 100644 index 0000000000..c2015f9e7c --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/MessageFactoryRegistry.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.client.message; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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; + +public class MessageFactoryRegistry +{ + 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("Unsupport MIME type of " + properties.getContentTypeAsString()); + } + else + { + return mf.createMessage(deliveryTag, redelivered, contentHeader, exchange, routingKey, bodies); + } + } + + public AbstractJMSMessage createMessage(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("Unsupport MIME type of " + mimeType); + } + else + { + return mf.createMessage(); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.java new file mode 100644 index 0000000000..1f61a661d4 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnexpectedBodyReceivedException.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.client.message; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * @todo Not used! Delete! + */ +public class UnexpectedBodyReceivedException extends AMQException +{ + public UnexpectedBodyReceivedException(String msg, Throwable t) + { + super(msg, t); + } + + public UnexpectedBodyReceivedException(String msg) + { + super(msg); + } + + public UnexpectedBodyReceivedException(AMQConstant errorCode, String msg) + { + super(errorCode, msg); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java b/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.java new file mode 100644 index 0000000000..5b199f2478 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/message/UnprocessedMessage.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.message; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.BasicReturnBody; +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 +{ + private long _bytesReceived = 0; + + private final BasicDeliverBody _deliverBody; + private final BasicReturnBody _bounceBody; // TODO: check change (gustavo) + private final int _channelId; + 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(int channelId, BasicDeliverBody deliverBody) + { + _deliverBody = deliverBody; + _channelId = channelId; + _bounceBody = null; + } + + + public UnprocessedMessage(int channelId, BasicReturnBody bounceBody) + { + _deliverBody = null; + _channelId = channelId; + _bounceBody = bounceBody; + } + + public void receiveBody(ContentBody body) //throws UnexpectedBodyReceivedException + { + + 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 boolean isAllBodyDataReceived() + { + return _bytesReceived == getContentHeader().bodySize; + } + + public BasicDeliverBody getDeliverBody() + { + return _deliverBody; + } + + public BasicReturnBody getBounceBody() + { + return _bounceBody; + } + + + public int getChannelId() + { + return _channelId; + } + + + public ContentHeaderBody getContentHeader() + { + return _contentHeader; + } + + public void setContentHeader(ContentHeaderBody contentHeader) + { + this._contentHeader = contentHeader; + } + + public List getBodies() + { + return _bodies; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java new file mode 100644 index 0000000000..e7ff5afceb --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -0,0 +1,732 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.SSLFilter; +import org.apache.mina.filter.codec.ProtocolCodecException; +import org.apache.mina.filter.codec.ProtocolCodecFilter; + +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.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.listener.SpecificMethodFrameListener; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionCloseOkBody; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.HeartbeatBody; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +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); + + /** + * 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; + + /** Defines the default timeout to use for synchronous protocol commands. */ + private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30; + + /** + * 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) + { + e.printStackTrace(); + } + + _protocolSession = new AMQProtocolSession(this, session, _connection, getStateManager()); + _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.")); + } + 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) + { + _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); + propagateExceptionToWaiters(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); + propagateExceptionToWaiters(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. + * + * @param e the exception to propagate + */ + public void propagateExceptionToWaiters(Exception e) + { + getStateManager().error(e); + if (!_frameListeners.isEmpty()) + { + final Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener ml = (AMQMethodListener) it.next(); + ml.error(e); + } + } + } + + private static int _messageReceivedCount; + + public void messageReceived(IoSession session, Object message) throws Exception + { + 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); + + switch (bodyFrame.getFrameType()) + { + case AMQMethodBody.TYPE: + + if (debug) + { + _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + frame); + } + + final AMQMethodEvent evt = + new AMQMethodEvent(frame.getChannel(), (AMQMethodBody) bodyFrame); + + try + { + + boolean wasAnyoneInterested = getStateManager().methodReceived(evt); + if (!_frameListeners.isEmpty()) + { + Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener listener = (AMQMethodListener) it.next(); + wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; + } + } + + if (!wasAnyoneInterested) + { + throw new AMQException("AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + + _frameListeners); + } + } + catch (AMQException e) + { + getStateManager().error(e); + if (!_frameListeners.isEmpty()) + { + Iterator it = _frameListeners.iterator(); + while (it.hasNext()) + { + final AMQMethodListener listener = (AMQMethodListener) it.next(); + listener.error(e); + } + } + + exceptionCaught(session, e); + } + + break; + + case ContentHeaderBody.TYPE: + + _protocolSession.messageContentHeaderReceived(frame.getChannel(), (ContentHeaderBody) bodyFrame); + break; + + case ContentBody.TYPE: + + _protocolSession.messageContentBodyReceived(frame.getChannel(), (ContentBody) bodyFrame); + break; + + case HeartbeatBody.TYPE: + + if (debug) + { + _logger.debug("Received heartbeat"); + } + + break; + + default: + + } + + _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); + } + + private static int _messagesOut; + + public void messageSent(IoSession session, Object message) throws Exception + { + final long sentMessages = _messagesOut++; + + final boolean debug = _logger.isDebugEnabled(); + + if (debug && ((sentMessages % 1000) == 0)) + { + _logger.debug("Sent " + _messagesOut + " protocol messages"); + } + + _connection.bytesSent(session.getWrittenBytes()); + if (debug) + { + _logger.debug("Sent frame " + message); + } + } + + /* + public void addFrameListener(AMQMethodListener listener) + { + _frameListeners.add(listener); + } + + public void removeFrameListener(AMQMethodListener listener) + { + _frameListeners.remove(listener); + } + */ + public void attainState(AMQState s) throws AMQException + { + getStateManager().attainState(s); + } + + /** + * 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 + { + _frameListeners.add(listener); + _protocolSession.writeFrame(frame); + + AMQMethodEvent e = listener.blockForFrame(timeout); + + return e; + // When control resumes before this line, a reply will have been received + // that matches the criteria defined in the blocking listener + } + catch (AMQException e) + { + throw e; + } + 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); + + // 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. + final AMQFrame frame = + ConnectionCloseBody.createAMQFrame(0, _protocolSession.getProtocolMajorVersion(), + _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor) + 0, // classId + 0, // methodId + AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client is closing the connection.")); // replyText + + 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 + { + if (_failoverLatch != null) + { + _failoverLatch.await(); + } + } + + public AMQShortString generateQueueName() + { + return _protocolSession.generateQueueName(); + } + + public CountDownLatch getFailoverLatch() + { + return _failoverLatch; + } + + public void setFailoverLatch(CountDownLatch failoverLatch) + { + _failoverLatch = failoverLatch; + } + + public AMQConnection getConnection() + { + return _connection; + } + + public AMQStateManager getStateManager() + { + return _stateManager; + } + + public void setStateManager(AMQStateManager stateManager) + { + _stateManager = stateManager; + if (_protocolSession != null) + { + _protocolSession.setStateManager(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(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java new file mode 100644 index 0000000000..5fe6ffe6c6 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -0,0 +1,459 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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.UnexpectedBodyReceivedException; +import org.apache.qpid.client.message.UnprocessedMessage; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.MainRegistry; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.framing.VersionSpecificRegistry; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +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 java.util.UUID; + +/** + * 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; + + private AMQStateManager _stateManager; + + 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. + */ + protected ConcurrentMap _channelId2UnprocessedMsgMap = new ConcurrentHashMap(); + + /** Counter to ensure unique queue names */ + protected int _queueId = 1; + protected final Object _queueIdLock = new Object(); + + private byte _protocolMinorVersion; + private byte _protocolMajorVersion; + private VersionSpecificRegistry _registry = + MainRegistry.getVersionSpecificRegistry(ProtocolVersion.getLatestSupportedVersion()); + + private final AMQConnection _connection; + + public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection) + { + this(protocolHandler, protocolSession, connection, new AMQStateManager()); + + } + + public AMQProtocolSession(AMQProtocolHandler protocolHandler, IoSession protocolSession, AMQConnection connection, + AMQStateManager stateManager) + { + _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); + _stateManager = stateManager; + _stateManager.setProtocolSession(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(ProtocolVersion.getLatestSupportedVersion())); + } + + 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 _stateManager; + } + + public void setStateManager(AMQStateManager stateManager) + { + _stateManager = stateManager; + } + + 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(UnprocessedMessage message) throws AMQException + { + _channelId2UnprocessedMsgMap.put(message.getChannelId(), message); + } + + public void messageContentHeaderReceived(int channelId, ContentHeaderBody contentHeader) throws AMQException + { + UnprocessedMessage msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId); + if (msg == null) + { + throw new AMQException("Error: received content header without having received a BasicDeliver frame first"); + } + + if (msg.getContentHeader() != null) + { + throw new AMQException( + "Error: received duplicate content header or did not receive correct number of content body frames"); + } + + msg.setContentHeader(contentHeader); + if (contentHeader.bodySize == 0) + { + deliverMessageToAMQSession(channelId, msg); + } + } + + public void messageContentBodyReceived(int channelId, ContentBody contentBody) throws AMQException + { + UnprocessedMessage msg = (UnprocessedMessage) _channelId2UnprocessedMsgMap.get(channelId); + if (msg == null) + { + throw new AMQException("Error: received content body without having received a JMSDeliver frame first"); + } + + if (msg.getContentHeader() == null) + { + _channelId2UnprocessedMsgMap.remove(channelId); + throw new AMQException("Error: received content body without having received a ContentHeader frame first"); + } + + /*try + {*/ + msg.receiveBody(contentBody); + /*} + catch (UnexpectedBodyReceivedException e) + { + _channelId2UnprocessedMsgMap.remove(channelId); + throw e; + }*/ + + if (msg.isAllBodyDataReceived()) + { + deliverMessageToAMQSession(channelId, msg); + } + } + + /** + * 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); + _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)); + } + catch (JMSException e) + { + throw new AMQException("JMSException received while closing session", e); + } + + return true; + } + else + { + return false; + } + } + + public AMQConnection getAMQConnection() + { + return (AMQConnection) _minaProtocolSession.getAttribute(AMQ_CONNECTION); + } + + public void closeProtocolSession() + { + closeProtocolSession(true); + } + + public void closeProtocolSession(boolean waitLast) + { + _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(); + future.join(LAST_WRITE_FUTURE_JOIN_TIMEOUT); + } + + public void failover(String host, int port) + { + _protocolHandler.failover(host, port); + } + + protected AMQShortString generateQueueName() + { + int id; + + return new AMQShortString("tmp_" + UUID.randomUUID()); + } + + /** @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); + } + + public void setProtocolVersion(final byte versionMajor, final byte versionMinor) + { + _protocolMajorVersion = versionMajor; + _protocolMinorVersion = versionMinor; + _registry = MainRegistry.getVersionSpecificRegistry(versionMajor, versionMinor); + } + + public byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } + + public byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public VersionSpecificRegistry getRegistry() + { + return _registry; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java new file mode 100644 index 0000000000..1badbb601c --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java @@ -0,0 +1,311 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.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 What is to stop a blocking method listener, receiving a second method whilst it is registered as a listener, + * overwriting the first one before the caller of the block method has had a chance to examine it? If one-shot + * behaviour is to be intended it should be enforced, perhaps by always returning false once the blocked for + * method has been received. + * + * @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 BlockingMethodFrameListener implements AMQMethodListener +{ + /** 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 channel id for the channel upon which this listener is waiting for a response. */ + protected int _channelId; + + /** Holds the incoming method. */ + protected AMQMethodEvent _doneEvt = 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 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); // throws AMQException; + + /** + * 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) // throws AMQException + { + AMQMethodBody method = evt.getMethod(); + + /*try + {*/ + boolean ready = (evt.getChannelId() == _channelId) && processMethod(evt.getChannelId(), method); + + 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 + { + _doneEvt = evt; + _ready = ready; + _receivedCondition.signal(); + } + finally + { + _lock.unlock(); + } + } + + return ready; + + /*} + catch (AMQException e) + { + error(e); + // we rethrow the error here, and the code in the frame dispatcher will go round + // each listener informing them that an exception has been thrown + throw e; + }*/ + } + + /** + * 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 + { + long nanoTimeout = TimeUnit.MILLISECONDS.toNanos(timeout); + + _lock.lock(); + + try + { + 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"); + _ready = true; + } + } + } + catch (InterruptedException e) + { + // 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 + { + _errorAck = true; + _errorConditionAck.signal(); + _error = null; + _lock.unlock(); + } + + return _doneEvt; + } + + /** + * This is a callback, called by the MINA dispatcher thread only. It is also called from within this + * class to avoid code repetition but again is only called by the MINA dispatcher thread. + * + * @param e + */ + 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 (_error == null) + { + _error = e; + } + else + { + System.err.println("WARNING: new error arrived while old one not yet processed"); + } + + try + { + _ready = true; + _receivedCondition.signal(); + + while (!_errorAck) + { + try + { + _errorConditionAck.await(); + } + catch (InterruptedException e1) + { + // + } + } + _errorAck = false; + } + finally + { + _lock.unlock(); + } + } + + public boolean equals(Object o) + { + + if (o instanceof BlockingMethodFrameListener) + { + BlockingMethodFrameListener other = (BlockingMethodFrameListener) o; + + return _channelId == other._channelId; + } + + return false; + } + + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java new file mode 100644 index 0000000000..35ea44a331 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatDiagnostics.java new file mode 100644 index 0000000000..d44faeab04 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java new file mode 100644 index 0000000000..93cc5e7ec3 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/AMQCallbackHandler.java new file mode 100644 index 0000000000..fbca444208 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java new file mode 100644 index 0000000000..140cbdeb75 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties new file mode 100644 index 0000000000..89ee8337f8 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties @@ -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. +# +CallbackHandler.CRAM-MD5-HASHED=org.apache.qpid.client.security.UsernameHashedPasswordCallbackHandler +CallbackHandler.CRAM-MD5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +CallbackHandler.PLAIN=org.apache.qpid.client.security.UsernamePasswordCallbackHandler diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java new file mode 100644 index 0000000000..803b34b7fa --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java @@ -0,0 +1,198 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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) + { + Security.insertProviderAt(new JCAProvider(factories), 0); + _logger.debug("Dynamic SASL provider added as a security provider"); + } + } + 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; + } + + factoriesToRegister.put(mechanism, (Class) clazz); + } + catch (Exception ex) + { + _logger.error("Error instantiating SaslClientFactory calss " + className + " - skipping"); + } + } + + return factoriesToRegister; + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/Final2/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties new file mode 100644 index 0000000000..1bff43142b --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java new file mode 100644 index 0000000000..5a2c5ac5c1 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.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.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.SaslClientFactory; + +import java.security.Provider; +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", 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/Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java new file mode 100644 index 0000000000..66176dac3c --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/UsernamePasswordCallbackHandler.java new file mode 100644 index 0000000000..c50c62710f --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClient.java new file mode 100644 index 0000000000..f8a25c630c --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/amqplain/AmqPlainSaslClientFactory.java new file mode 100644 index 0000000000..30cc786890 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/client/security/crammd5hashed/CRAMMD5HashedSaslClientFactory.java new file mode 100644 index 0000000000..22bb1ac156 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQState.java new file mode 100644 index 0000000000..4996f59345 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQState.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.client.state; + +/** + * States used in the AMQ protocol. Used by the finite state machine to determine + * valid responses. + */ +public class AMQState +{ + 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; + } + + public static final AMQState CONNECTION_NOT_STARTED = new AMQState(1, "CONNECTION_NOT_STARTED"); + + public static final AMQState CONNECTION_NOT_TUNED = new AMQState(2, "CONNECTION_NOT_TUNED"); + + public static final AMQState CONNECTION_NOT_OPENED = new AMQState(3, "CONNECTION_NOT_OPENED"); + + public static final AMQState CONNECTION_OPEN = new AMQState(4, "CONNECTION_OPEN"); + + public static final AMQState CONNECTION_CLOSING = new AMQState(5, "CONNECTION_CLOSING"); + + public static final AMQState CONNECTION_CLOSED = new AMQState(6, "CONNECTION_CLOSED"); + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateChangedEvent.java new file mode 100644 index 0000000000..edef54ccd6 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateListener.java new file mode 100644 index 0000000000..110471aad0 --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java new file mode 100644 index 0000000000..227f23b540 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.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.client.state; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.handler.BasicCancelOkMethodHandler; +import org.apache.qpid.client.handler.BasicDeliverMethodHandler; +import org.apache.qpid.client.handler.BasicReturnMethodHandler; +import org.apache.qpid.client.handler.ChannelCloseMethodHandler; +import org.apache.qpid.client.handler.ChannelCloseOkMethodHandler; +import org.apache.qpid.client.handler.ChannelFlowOkMethodHandler; +import org.apache.qpid.client.handler.ConnectionCloseMethodHandler; +import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler; +import org.apache.qpid.client.handler.ConnectionSecureMethodHandler; +import org.apache.qpid.client.handler.ConnectionStartMethodHandler; +import org.apache.qpid.client.handler.ConnectionTuneMethodHandler; +import org.apache.qpid.client.handler.ExchangeBoundOkMethodHandler; +import org.apache.qpid.client.handler.QueueDeleteOkMethodHandler; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicCancelOkBody; +import org.apache.qpid.framing.BasicDeliverBody; +import org.apache.qpid.framing.BasicReturnBody; +import org.apache.qpid.framing.ChannelCloseBody; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ChannelFlowOkBody; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.ConnectionOpenOkBody; +import org.apache.qpid.framing.ConnectionSecureBody; +import org.apache.qpid.framing.ConnectionStartBody; +import org.apache.qpid.framing.ConnectionTuneBody; +import org.apache.qpid.framing.ExchangeBoundOkBody; +import org.apache.qpid.framing.QueueDeleteOkBody; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.protocol.AMQMethodListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * 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 = LoggerFactory.getLogger(AMQStateManager.class); + + private 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. + */ + protected final Map _state2HandlersMap = new HashMap(); + + private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet(); + private final Object _stateLock = new Object(); + private static final long MAXIMUM_STATE_WAIT_TIME = 30000L; + + public AMQStateManager() + { + this(null); + } + + public AMQStateManager(AMQProtocolSession protocolSession) + { + this(AMQState.CONNECTION_NOT_STARTED, true, protocolSession); + } + + protected AMQStateManager(AMQState state, boolean register, AMQProtocolSession protocolSession) + { + _protocolSession = protocolSession; + _currentState = state; + if (register) + { + registerListeners(); + } + } + + protected void registerListeners() + { + Map frame2handlerMap = new HashMap(); + + // we need to register a map for the null (i.e. all state) handlers otherwise you get + // a stack overflow in the handler searching code when you present it with a frame for which + // no handlers are registered + // + _state2HandlersMap.put(null, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap); + + // + // ConnectionOpen handlers + // + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandler.getInstance()); + frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance()); + frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance()); + frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance()); + frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance()); + frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance()); + frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); + } + + 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; + _stateLock.notifyAll(); + } + } + + public void error(Exception e) + { + _logger.debug("State manager receive error notification: " + e); + synchronized (_stateListeners) + { + final Iterator it = _stateListeners.iterator(); + while (it.hasNext()) + { + final StateListener l = (StateListener) it.next(); + l.error(e); + } + } + } + + public boolean methodReceived(AMQMethodEvent evt) throws AMQException + { + StateAwareMethodListener handler = findStateTransitionHandler(_currentState, evt.getMethod()); + if (handler != null) + { + handler.methodReceived(this, _protocolSession, evt); + + return true; + } + + return false; + } + + protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, AMQMethodBody frame) + // throws IllegalStateTransitionException + { + final Class clazz = frame.getClass(); + if (_logger.isDebugEnabled()) + { + _logger.debug("Looking for state[" + currentState + "] transition handler for frame " + clazz); + } + + final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState); + + if (classToHandlerMap == null) + { + // if no specialised per state handler is registered look for a + // handler registered for "all" states + return findStateTransitionHandler(null, frame); + } + + final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz); + if (handler == null) + { + if (currentState == null) + { + _logger.debug("No state transition handler defined for receiving frame " + frame); + + return null; + } + else + { + // if no specialised per state handler is registered look for a + // handler registered for "all" states + return findStateTransitionHandler(null, frame); + } + } + else + { + return handler; + } + } + + public void attainState(final AMQState s) throws AMQException + { + synchronized (_stateLock) + { + final long waitUntilTime = System.currentTimeMillis() + MAXIMUM_STATE_WAIT_TIME; + long waitTime = MAXIMUM_STATE_WAIT_TIME; + + while ((_currentState != s) && (waitTime > 0)) + { + try + { + _stateLock.wait(MAXIMUM_STATE_WAIT_TIME); + } + catch (InterruptedException e) + { + _logger.warn("Thread interrupted"); + } + + if (_currentState != s) + { + waitTime = waitUntilTime - System.currentTimeMillis(); + } + } + + if (_currentState != s) + { + _logger.warn("State not achieved within permitted time. Current state " + _currentState + + ", desired state: " + s); + throw new AMQException("State not achieved within permitted time. Current state " + _currentState + + ", desired state: " + s); + } + } + + // at this point the state will have changed. + } + + public AMQProtocolSession getProtocolSession() + { + return _protocolSession; + } + + public void setProtocolSession(AMQProtocolSession session) + { + _protocolSession = session; + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.java new file mode 100644 index 0000000000..41fa1ba704 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/IllegalStateTransitionException.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.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/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.java new file mode 100644 index 0000000000..b3932533ce --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateAwareMethodListener.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.state; + +import org.apache.qpid.AMQException; +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, + AMQMethodEvent evt) throws AMQException; +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateListener.java new file mode 100644 index 0000000000..df207a0a23 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/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.client.state; + +import org.apache.qpid.AMQException; + +public interface StateListener +{ + void stateChanged(AMQState oldState, AMQState newState) throws AMQException; + + void error(Throwable t); +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java new file mode 100644 index 0000000000..73d5a8a3d6 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.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.client.state; + +import org.apache.qpid.AMQException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Waits for a particular state to be reached. + */ +public class StateWaiter implements StateListener +{ + private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class); + + private final AMQState _state; + + private volatile boolean _newStateAchieved; + + private volatile Throwable _throwable; + + private final Object _monitor = new Object(); + private static final long TIME_OUT = 1000 * 60 * 2; + + public StateWaiter(AMQState state) + { + _state = state; + } + + public void waituntilStateHasChanged() throws AMQException + { + synchronized (_monitor) + { + // + // The guard is required in case we are woken up by a spurious + // notify(). + // + while (!_newStateAchieved && (_throwable == null)) + { + try + { + _logger.debug("State " + _state + " not achieved so waiting..."); + _monitor.wait(TIME_OUT); + // fixme this won't cause the timeout to exit the loop. need to set _throwable + } + catch (InterruptedException e) + { + _logger.debug("Interrupted exception caught while waiting: " + e, e); + } + } + } + + if (_throwable != null) + { + _logger.debug("Throwable reached state waiter: " + _throwable); + if (_throwable instanceof AMQException) + { + throw (AMQException) _throwable; + } + else + { + throw new AMQException("Error: " + _throwable, _throwable); // FIXME: this will wrap FailoverException in throwable which will prevent it being caught. + } + } + } + + public void stateChanged(AMQState oldState, AMQState newState) + { + synchronized (_monitor) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("stateChanged called changing from :" + oldState + " to :" + newState); + } + + if (_state == newState) + { + _newStateAchieved = true; + + if (_logger.isDebugEnabled()) + { + _logger.debug("New state reached so notifying monitor"); + } + + _monitor.notifyAll(); + } + } + } + + public void error(Throwable t) + { + synchronized (_monitor) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("exceptionThrown called"); + } + + _throwable = t; + _monitor.notifyAll(); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java b/Final2/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java new file mode 100644 index 0000000000..4a4f4a0a38 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.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.state.listener; + +import org.apache.qpid.AMQException; +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) //throws AMQException + { + + //equiv to: (frame instanceof _expectedClass) + return _expectedClass.isInstance(frame); + } + + public boolean equals(Object o) + { + if (o instanceof SpecificMethodFrameListener) + { + SpecificMethodFrameListener other = (SpecificMethodFrameListener) o; + + // here we need to check if the two classes are the same. + return (_channelId == other._channelId) && (_expectedClass.equals(other._expectedClass)); + } + + return false; + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.java new file mode 100644 index 0000000000..da16baaad9 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQNoTransportForProtocolException.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.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. + *
+ */ +public class AMQNoTransportForProtocolException extends AMQTransportConnectionException +{ + BrokerDetails _details; + + public AMQNoTransportForProtocolException(BrokerDetails details) + { + this(details, "No Transport exists for specified broker protocol"); + } + + public AMQNoTransportForProtocolException(BrokerDetails details, String message) + { + super(null, message, null); + + _details = details; + } + + public String toString() + { + if (_details != null) + { + return super.toString() + _details.toString(); + } + else + { + return super.toString(); + } + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.java new file mode 100644 index 0000000000..24b4e03b39 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/AMQTransportConnectionException.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.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. + *
+ */ +public class AMQTransportConnectionException extends AMQException +{ + public AMQTransportConnectionException(AMQConstant errorCode, String message, Throwable cause) + { + super(errorCode, message, cause); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/ITransportConnection.java new file mode 100644 index 0000000000..7a24d6e15a --- /dev/null +++ b/Final2/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/Final2/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java new file mode 100644 index 0000000000..5482e48699 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.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.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.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; + +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 = 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/Final2/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java new file mode 100644 index 0000000000..1d0d6a3491 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -0,0 +1,318 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.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.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.Iterator; +import java.util.Map; + +/** + * 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 Logger _logger = LoggerFactory.getLogger(TransportConnection.class); + + private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; + + public static ITransportConnection getInstance(BrokerDetails details) throws AMQTransportConnectionException + { + int transport = getTransport(details.getTransport()); + + if (transport == -1) + { + throw new AMQNoTransportForProtocolException(details); + } + + if (transport == _currentInstance) + { + if (transport == VM) + { + if (_currentVMPort == details.getPort()) + { + return _instance; + } + } + else + { + return _instance; + } + } + + _currentInstance = transport; + + switch (transport) + { + + case TCP: + _instance = 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.error("Using Qpid NIO - sysproperty 'qpidnio' is set."); + // result = new org.apache.qpid.nio.SocketConnector(); // non-blocking connector + } + // 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; + } + }); + break; + + case VM: + { + _instance = getVMTransport(details, Boolean.getBoolean("amqj.AutoCreateVMBroker")); + break; + } + } + + return _instance; + } + + private static int getTransport(String transport) + { + 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) + { + createVMBroker(port); + } + 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", null); + amqbce.initCause(e); + throw amqbce; + } + + return provider; + } + + public static void killAllVMBrokers() + { + _logger.info("Killing all VM Brokers"); + _acceptor.unbindAll(); + synchronized (_inVmPipeAddress) + { + Iterator keys = _inVmPipeAddress.keySet().iterator(); + + while (keys.hasNext()) + { + int id = (Integer) keys.next(); + _inVmPipeAddress.remove(id); + } + } + } + + 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); + _acceptor.unbind(pipe); + } + } + } + +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java new file mode 100644 index 0000000000..d9137dc8b1 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.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.transport; + +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IoServiceConfig; +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.PoolingFilter; +import org.apache.qpid.pool.ReferenceCountingExecutorService; +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 VmPipeConnector(); + 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/Final2/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/Final2/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java new file mode 100644 index 0000000000..579b0d9e90 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.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.client.util; + +import java.util.Iterator; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * 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 BlockingQueue _queue = new LinkedBlockingQueue(); + + 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 poll(long time, TimeUnit unit) throws InterruptedException + { + Object o = _queue.poll(time, unit); + if (o != null && _listener != null) + { + synchronized (_listener) + { + if (_count-- == _flowControlLowThreshold) + { + _listener.underThreshold(_count); + } + } + } + + return o; + } + + public void add(Object o) + { + _queue.add(o); + if (_listener != null) + { + synchronized (_listener) + { + if (++_count == _flowControlHighThreshold) + { + _listener.aboveThreshold(_count); + } + } + } + } + + public Iterator iterator() + { + return _queue.iterator(); + } +} diff --git a/Final2/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java b/Final2/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.java new file mode 100644 index 0000000000..1791e7ede3 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/client/vmbroker/AMQVMBrokerCreationException.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.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. + *
+ */ +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/Final2/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java b/Final2/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.java new file mode 100644 index 0000000000..91f7710025 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/jms/BrokerDetails.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.jms; + +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 int DEFAULT_PORT = 5672; + + 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/Final2/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/Final2/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java new file mode 100644 index 0000000000..a46c7f3cd5 --- /dev/null +++ b/Final2/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.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.jndi; + +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; + +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 java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +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); + _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) + { + 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 (URLSyntaxException urlse) + { + _logger.warn("Unable to destination:" + 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) + { + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, new AMQShortString((String) value)); + } + 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/Final2/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java b/Final2/java/client/src/main/java/org/apache/qpid/jndi/ReadOnlyContext.java new file mode 100644 index 0000000000..1719ea1219 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java b/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindConnectionFactory.java new file mode 100644 index 0000000000..2c08f1e34a --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java b/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindQueue.java new file mode 100644 index 0000000000..10e8b94311 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java b/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/JNDIBindTopic.java new file mode 100644 index 0000000000..ca071c1187 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt b/Final2/java/client/src/old_test/java/org/apache/qpid/IBMPerfTest/README.txt new file mode 100644 index 0000000000..95ee9f9c77 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java b/Final2/java/client/src/old_test/java/org/apache/qpid/cluster/Client.java new file mode 100644 index 0000000000..cf8059a143 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/codec/BasicDeliverTest.java new file mode 100644 index 0000000000..1db7e200bd --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/codec/Client.java b/Final2/java/client/src/old_test/java/org/apache/qpid/codec/Client.java new file mode 100644 index 0000000000..3886021277 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/codec/Server.java b/Final2/java/client/src/old_test/java/org/apache/qpid/codec/Server.java new file mode 100644 index 0000000000..fa4295e0b2 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/AMQConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..cac0064785 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/AbstractConfig.java new file mode 100644 index 0000000000..04381d66a0 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..a9984eb09a --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/Connector.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/Connector.java new file mode 100644 index 0000000000..ff2377f087 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/ConnectorConfig.java new file mode 100644 index 0000000000..b120ed3f12 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java b/Final2/java/client/src/old_test/java/org/apache/qpid/config/JBossConnectionFactoryInitialiser.java new file mode 100644 index 0000000000..44285efd96 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/flow/ChannelFlowTest.java new file mode 100644 index 0000000000..cb8adae18c --- /dev/null +++ b/Final2/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(); //To change body of catch statement use File | Settings | File Templates. + } + catch (AMQException e) + { + System.err.println("Error in client: " + e); + e.printStackTrace(); + } + + //System.exit(0); + } +} diff --git a/Final2/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java b/Final2/java/client/src/old_test/java/org/apache/qpid/fragmentation/TestLargeSubscriber.java new file mode 100644 index 0000000000..b0cde22349 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java b/Final2/java/client/src/old_test/java/org/apache/qpid/headers/Listener.java new file mode 100644 index 0000000000..cb5caefc1e --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java b/Final2/java/client/src/old_test/java/org/apache/qpid/headers/MessageFactory.java new file mode 100644 index 0000000000..a2d575fdd4 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java b/Final2/java/client/src/old_test/java/org/apache/qpid/headers/Publisher.java new file mode 100644 index 0000000000..d9ef702c48 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java b/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Bind.java new file mode 100644 index 0000000000..ee6a12c233 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java b/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Lookup.java new file mode 100644 index 0000000000..1c9d8b0fd5 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java b/Final2/java/client/src/old_test/java/org/apache/qpid/jndi/referenceable/Unbind.java new file mode 100644 index 0000000000..1acead674c --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/latency/LatencyTest.java new file mode 100644 index 0000000000..4865a68dc4 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/mina/AcceptorTest.java new file mode 100644 index 0000000000..f0ac0e6902 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/mina/BlockingAcceptorTest.java new file mode 100644 index 0000000000..bfe29c47e6 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/mina/WriterTest.java new file mode 100644 index 0000000000..910345624f --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/multiconsumer/AMQTest.java new file mode 100644 index 0000000000..db02b9954a --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java b/Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestPublisher.java new file mode 100644 index 0000000000..33891142b5 --- /dev/null +++ b/Final2/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(); //To change body of catch statement use File | Settings | File Templates. + } + } + + 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(); //To change body of catch statement use File | Settings | File Templates. + } + catch (AMQException e) + { + System.err.println("Error in client: " + e); + e.printStackTrace(); + } + + //System.exit(0); + } +} diff --git a/Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java b/Final2/java/client/src/old_test/java/org/apache/qpid/pubsub1/TestSubscriber.java new file mode 100644 index 0000000000..450d9b3914 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/client/connection/TestManyConnections.java b/Final2/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/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java b/Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/PropertiesFileInitialContextFactoryTest.java new file mode 100644 index 0000000000..5ab5722146 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties b/Final2/java/client/src/old_test/java/org/apache/qpid/test/unit/jndi/example.properties new file mode 100644 index 0000000000..ea9dc5ae0e --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Config.java b/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Config.java new file mode 100644 index 0000000000..bb740f9094 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java b/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Listener.java new file mode 100644 index 0000000000..47c608cfe4 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java b/Final2/java/client/src/old_test/java/org/apache/qpid/topic/MessageFactory.java new file mode 100644 index 0000000000..39d64069d1 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java b/Final2/java/client/src/old_test/java/org/apache/qpid/topic/Publisher.java new file mode 100644 index 0000000000..d788029ee9 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java b/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Config.java new file mode 100644 index 0000000000..bd104e5407 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java b/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Ping.java new file mode 100644 index 0000000000..8f15bf089e --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java b/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Pong.java new file mode 100644 index 0000000000..f4f4b20d7c --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java b/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Relay.java new file mode 100644 index 0000000000..cede95e5f0 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java b/Final2/java/client/src/old_test/java/org/apache/qpid/transacted/Start.java new file mode 100644 index 0000000000..de718d828a --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java b/Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceProvider.java new file mode 100644 index 0000000000..71d806b338 --- /dev/null +++ b/Final2/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/Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java b/Final2/java/client/src/old_test/java/org/apache/qpid/weblogic/ServiceRequestingClient.java new file mode 100644 index 0000000000..2f64a1dde5 --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java b/Final2/java/client/src/test/java/org/apache/mina/transport/vmpipe/support/VmPipeIdleStatusChecker.java new file mode 100644 index 0000000000..5323ad28bf --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java new file mode 100644 index 0000000000..fe418535d6 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.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.client; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.jms.Session; +import org.apache.qpid.client.transport.TransportConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class tests all the alerts an AMQQueue can throw based on threshold + * values of different parameters + */ +public class AMQQueueDeferredOrderingTest extends TestCase +{ + + private static final int NUM_MESSAGES = 1000; + + private AMQConnection con; + private Session session; + private AMQQueue queue; + private MessageConsumer consumer; + + private static final Logger _logger = LoggerFactory.getLogger(AMQQueueDeferredOrderingTest.class); + + private ASyncProducer producerThread; + private static final String BROKER = "vm://:1"; + + private class ASyncProducer extends Thread + { + + private MessageProducer producer; + private final Logger _logger = LoggerFactory.getLogger(ASyncProducer.class); + private Session session; + private int start; + private int end; + + public ASyncProducer(AMQQueue q, int start, int end) throws Exception + { + this.session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + this._logger.info("Create Consumer of Q1"); + this.producer = this.session.createProducer(q); + this.start = start; + this.end = end; + } + + public void run() + { + try + { + this._logger.info("Starting to send messages"); + for (int i = start; i < end && !interrupted(); i++) + { + producer.send(session.createTextMessage(Integer.toString(i))); + } + this._logger.info("Sent " + (end - start) + " messages"); + } + catch (JMSException e) + { + throw new RuntimeException(e); + } + } + } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + _logger.info("Create Connection"); + con = new AMQConnection(BROKER, "guest", "guest", "OrderingTest", "test"); + _logger.info("Create Session"); + session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + _logger.info("Create Q"); + queue = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q"), new AMQShortString("Q"), + false, true); + _logger.info("Create Consumer of Q"); + consumer = session.createConsumer(queue); + _logger.info("Start Connection"); + con.start(); + } + + public void testPausedOrder() throws Exception + { + + // Setup initial messages + _logger.info("Creating first producer thread"); + producerThread = new ASyncProducer(queue, 0, NUM_MESSAGES / 2); + producerThread.start(); + // Wait for them to be done + producerThread.join(); + + // Setup second set of messages to produce while we consume + _logger.info("Creating second producer thread"); + producerThread = new ASyncProducer(queue, NUM_MESSAGES / 2, NUM_MESSAGES); + producerThread.start(); + + // Start consuming and checking they're in order + _logger.info("Consuming messages"); + for (int i = 0; i < NUM_MESSAGES; i++) + { + Message msg = consumer.receive(3000); + assertNotNull("Message should not be null", msg); + assertTrue("Message should be a text message", msg instanceof TextMessage); + assertEquals("Message content does not match expected", Integer.toString(i), ((TextMessage) msg).getText()); + } + } + + protected void tearDown() throws Exception + { + _logger.info("Interuptting producer thread"); + producerThread.interrupt(); + _logger.info("Closing connection"); + con.close(); + + TransportConnection.killAllVMBrokers(); + super.tearDown(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(AMQQueueDeferredOrderingTest.class); + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java new file mode 100644 index 0000000000..7cca22de6c --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/DispatcherTest.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +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.naming.Context; +import javax.naming.spi.InitialContextFactory; + +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery queue + *

    + * The message delivery process: + * Mina puts a message on _queue in AMQSession and the dispatcher thread take()s + * from here and dispatches to the _consumers. If the _consumer doesn't have a message listener set at connection start + * then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple consumers on a + * session can run in any order and a synchronous put/poll will block the dispatcher). + *

    + * When setting the message listener later the _synchronousQueue is just poll()'ed and the first message delivered + * the remaining messages will be left on the queue and lost, subsequent messages on the session will arrive first. + */ +public class DispatcherTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(DispatcherTest.class); + + Context _context; + + private static final int MSG_COUNT = 6; + private int _receivedCount = 0; + private int _receivedCountWhileStopped = 0; + private Connection _clientConnection, _producerConnection; + private MessageConsumer _consumer; + MessageProducer _producer; + Session _clientSession, _producerSession; + + private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(1); // all messages Sent Lock + private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(1); // all messages Sent Lock + + private volatile boolean _connectionStopped = false; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + InitialContextFactory factory = new PropertiesFileInitialContextFactory(); + + Hashtable env = new Hashtable(); + + env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'"); + env.put("queue.queue", "MessageListenerTest"); + + _context = factory.getInitialContext(env); + + Queue queue = (Queue) _context.lookup("queue"); + + // Create Client 1 + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _consumer = _clientSession.createConsumer(queue); + + // Create Producer + _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _producerConnection.start(); + + _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _producer = _producerSession.createProducer(queue); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + _producer.send(_producerSession.createTextMessage("Message " + msg)); + } + } + + protected void tearDown() throws Exception + { + + _clientConnection.close(); + + _producerConnection.close(); + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testAsynchronousRecieve() + { + _logger.info("Test Start"); + + assertTrue(!((AMQConnection) _clientConnection).started()); + + // Set default Message Listener + try + { + _consumer.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 1 ML 1 Received Message(" + _receivedCount + "):" + message); + + _receivedCount++; + + if (_receivedCount == MSG_COUNT) + { + _allFirstMessagesSent.countDown(); + } + + if (_connectionStopped) + { + _logger.info("Running with Message:" + _receivedCount); + } + + if (_connectionStopped && (_allFirstMessagesSent.getCount() == 0)) + { + _receivedCountWhileStopped++; + } + + if (_allFirstMessagesSent.getCount() == 0) + { + if (_receivedCount == (MSG_COUNT * 2)) + { + _allSecondMessagesSent.countDown(); + } + } + } + }); + + assertTrue("Connecion should not be started", !((AMQConnection) _clientConnection).started()); + _clientConnection.start(); + } + catch (JMSException e) + { + _logger.error("Error Setting Default ML on consumer1"); + } + + try + { + _allFirstMessagesSent.await(1000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + + try + { + assertTrue("Connecion should be started", ((AMQConnection) _clientConnection).started()); + _clientConnection.stop(); + _connectionStopped = true; + } + catch (JMSException e) + { + _logger.error("Error stopping connection"); + } + + try + { + _logger.error("Send additional messages"); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + _producer.send(_producerSession.createTextMessage("Message " + msg)); + } + } + catch (JMSException e) + { + _logger.error("Unable to send additional messages", e); + } + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + // ignore + } + + try + { + _logger.info("Restarting connection"); + + _connectionStopped = false; + _clientConnection.start(); + } + catch (JMSException e) + { + _logger.error("Error Setting Better ML on consumer1", e); + } + + _logger.info("Waiting upto 2 seconds for messages"); + + try + { + _allSecondMessagesSent.await(1000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + + assertEquals("Messages not received correctly", 0, _allFirstMessagesSent.getCount()); + assertEquals("Messages not received correctly", 0, _allSecondMessagesSent.getCount()); + assertEquals("Client didn't get all messages", MSG_COUNT * 2, _receivedCount); + assertEquals("Messages received while stopped is not 0", 0, _receivedCountWhileStopped); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(DispatcherTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.java new file mode 100644 index 0000000000..7461f6c200 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerImmediatePrefetch.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; + +/** + * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery + * queue

    The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread + * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at + * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple + * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher).

    When setting + * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining + * messages will be left on the queue and lost, subsequent messages on the session will arrive first. + */ +public class MessageListenerMultiConsumerImmediatePrefetch extends MessageListenerMultiConsumerTest +{ + protected void setUp() throws Exception + { + System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true"); + super.setUp(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MessageListenerMultiConsumerImmediatePrefetch.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java new file mode 100644 index 0000000000..20632e245f --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +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.naming.Context; +import javax.naming.spi.InitialContextFactory; + +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery + * queue

    The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread + * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at + * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple + * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher).

    When setting + * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining + * messages will be left on the queue and lost, subsequent messages on the session will arrive first. + */ +public class MessageListenerMultiConsumerTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(MessageListenerMultiConsumerTest.class); + + Context _context; + + private static final int MSG_COUNT = 6; + private int receivedCount1 = 0; + private int receivedCount2 = 0; + private Connection _clientConnection; + private MessageConsumer _consumer1; + private MessageConsumer _consumer2; + private Session _clientSession1; + private Queue _queue; + private final CountDownLatch _allMessagesSent = new CountDownLatch(2); // all messages Sent Lock + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + InitialContextFactory factory = new PropertiesFileInitialContextFactory(); + + Hashtable env = new Hashtable(); + + env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'"); + env.put("queue.queue", "direct://amq.direct//" + this.getClass().getName()); + + _context = factory.getInitialContext(env); + + _queue = (Queue) _context.lookup("queue"); + + // Create Client 1 + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientConnection.start(); + + _clientSession1 = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _consumer1 = _clientSession1.createConsumer(_queue); + + // Create Client 2 + Session clientSession2 = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _consumer2 = clientSession2.createConsumer(_queue); + + // Create Producer + Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + producerConnection.start(); + + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageProducer producer = producerSession.createProducer(_queue); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + producer.send(producerSession.createTextMessage("Message " + msg)); + } + + producerConnection.close(); + + } + + protected void tearDown() throws Exception + { + _clientConnection.close(); + + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testRecieveInterleaved() throws Exception + { + int msg = 0; + int MAX_LOOPS = MSG_COUNT * 2; + for (int loops = 0; (msg < MSG_COUNT) || (loops < MAX_LOOPS); loops++) + { + + if (_consumer1.receive(100) != null) + { + msg++; + } + + if (_consumer2.receive(100) != null) + { + msg++; + } + } + + assertEquals("Not all messages received.", MSG_COUNT, msg); + } + + public void testAsynchronousRecieve() throws Exception + { + _consumer1.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 1 Received Message(" + receivedCount1 + "):" + message); + + receivedCount1++; + + if (receivedCount1 == (MSG_COUNT / 2)) + { + _allMessagesSent.countDown(); + } + + } + }); + + _consumer2.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 2 Received Message(" + receivedCount2 + "):" + message); + + receivedCount2++; + if (receivedCount2 == (MSG_COUNT / 2)) + { + _allMessagesSent.countDown(); + } + } + }); + + _logger.info("Waiting upto 2 seconds for messages"); + + try + { + _allMessagesSent.await(4000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + + assertEquals(MSG_COUNT, receivedCount1 + receivedCount2); + } + + public void testRecieveC2Only() throws Exception + { + if ( + !Boolean.parseBoolean( + System.getProperties().getProperty(AMQSession.IMMEDIATE_PREFETCH, + AMQSession.IMMEDIATE_PREFETCH_DEFAULT))) + { + _logger.info("Performing Receive only on C2"); + for (int msg = 0; msg < MSG_COUNT; msg++) + { + assertTrue(MSG_COUNT + " msg should be received. Only received:" + msg, _consumer2.receive(1000) != null); + } + } + } + + public void testRecieveBoth() throws Exception + { + if ( + !Boolean.parseBoolean( + System.getProperties().getProperty(AMQSession.IMMEDIATE_PREFETCH, + AMQSession.IMMEDIATE_PREFETCH_DEFAULT))) + { + _logger.info("Performing Receive only with two consumers on one session "); + + MessageConsumer consumer2 = _clientSession1.createConsumer(_queue); + + for (int msg = 0; msg < (MSG_COUNT / 2); msg++) + { + + assertTrue(_consumer1.receive() != null); + } + + for (int msg = 0; msg < (MSG_COUNT / 2); msg++) + { + assertTrue(consumer2.receive() != null); + } + } + else + { + _logger.info("Performing Receive only on both C1 and C2"); + + for (int msg = 0; msg < (MSG_COUNT / 2); msg++) + { + + assertTrue(_consumer1.receive() != null); + } + + for (int msg = 0; msg < (MSG_COUNT / 2); msg++) + { + assertTrue(_consumer2.receive() != null); + } + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MessageListenerMultiConsumerTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.java new file mode 100644 index 0000000000..87630fad5b --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/MessageListenerTest.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; + +import junit.framework.TestCase; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +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.naming.Context; +import javax.naming.spi.InitialContextFactory; + +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery + * queue

    The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread + * take()s from here and dispatches to the _consumers. If the _consumer doesn't have a message listener set at + * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple + * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher).

    When setting + * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining + * messages will be left on the queue and lost, subsequent messages on the session will arrive first. + */ +public class MessageListenerTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(MessageListenerTest.class); + + Context _context; + + private static final int MSG_COUNT = 5; + private int receivedCount = 0; + private MessageConsumer _consumer; + private Connection _clientConnection; + private CountDownLatch _awaitMessages = new CountDownLatch(MSG_COUNT); + private static final String BROKER = "vm://:1"; + private static final String VHOST = "test"; + + protected void setUp() throws Exception + { + super.setUp(); + if (BROKER.contains("vm://")) + { + TransportConnection.createVMBroker(1); + } + + InitialContextFactory factory = new PropertiesFileInitialContextFactory(); + + Hashtable env = new Hashtable(); + + env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/" + VHOST + "?brokerlist='" + BROKER + "'"); + env.put("queue.queue", "MessageListenerTest"); + + _context = factory.getInitialContext(env); + + Queue queue = (Queue) _context.lookup("queue"); + + // Create Client + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientConnection.start(); + + Session clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _consumer = clientSession.createConsumer(queue); + + // Create Producer + + Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + producerConnection.start(); + + Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageProducer producer = producerSession.createProducer(queue); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + producer.send(producerSession.createTextMessage("Message " + msg)); + } + + producerConnection.close(); + + } + + protected void tearDown() throws Exception + { + _clientConnection.close(); + + super.tearDown(); + if (BROKER.contains("vm://")) + { + TransportConnection.killAllVMBrokers(); + } + } + + public void testSynchronousRecieve() throws Exception + { + for (int msg = 0; msg < MSG_COUNT; msg++) + { + assertTrue(_consumer.receive(2000) != null); + } + } + + public void testAsynchronousRecieve() throws Exception + { + _consumer.setMessageListener(this); + + _logger.info("Waiting 3 seconds for messages"); + + try + { + _awaitMessages.await(3000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + // Should have recieved all async messages + assertEquals(MSG_COUNT, receivedCount); + + } + + public void testRecieveTheUseMessageListener() throws Exception + { + + _logger.error("Test disabled as initial receive is not called first"); + // Perform initial receive to start connection + // assertTrue(_consumer.receive(2000) != null); + // receivedCount++; + + // Sleep to ensure remaining 4 msgs end up on _synchronousQueue + // Thread.sleep(1000); + + // Set the message listener and wait for the messages to come in. + _consumer.setMessageListener(this); + + _logger.info("Waiting 3 seconds for messages"); + + try + { + _awaitMessages.await(3000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + // Should have recieved all async messages + assertEquals(MSG_COUNT, receivedCount); + + } + + public void onMessage(Message message) + { + _logger.info("Received Message(" + receivedCount + "):" + message); + + receivedCount++; + _awaitMessages.countDown(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MessageListenerTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java new file mode 100644 index 0000000000..21f3e273aa --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.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.client; + +import junit.framework.TestCase; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jndi.PropertiesFileInitialContextFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +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.naming.Context; +import javax.naming.spi.InitialContextFactory; + +import java.util.Hashtable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * QPID-293 Setting MessageListener after connection has started can cause messages to be "lost" on a internal delivery + * queue

    The message delivery process: Mina puts a message on _queue in AMQSession and the dispatcher thread + * take()s from here and dispatches to the _consumers. If the _consumer1 doesn't have a message listener set at + * connection start then messages are stored on _synchronousQueue (which needs to be > 1 to pass JMS TCK as multiple + * consumers on a session can run in any order and a synchronous put/poll will block the dispatcher).

    When setting + * the message listener later the _synchronousQueue is just poll()'ed and the first message delivered the remaining + * messages will be left on the queue and lost, subsequent messages on the session will arrive first. + */ +public class ResetMessageListenerTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(ResetMessageListenerTest.class); + + Context _context; + + private static final int MSG_COUNT = 6; + private int receivedCount1ML1 = 0; + private int receivedCount1ML2 = 0; + private int receivedCount2 = 0; + private Connection _clientConnection, _producerConnection; + private MessageConsumer _consumer1; + private MessageConsumer _consumer2; + MessageProducer _producer; + Session _clientSession, _producerSession; + + private final CountDownLatch _allFirstMessagesSent = new CountDownLatch(2); // all messages Sent Lock + private final CountDownLatch _allSecondMessagesSent = new CountDownLatch(2); // all messages Sent Lock + + private String oldImmediatePrefetch; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + oldImmediatePrefetch = System.getProperty(AMQSession.IMMEDIATE_PREFETCH); + System.setProperty(AMQSession.IMMEDIATE_PREFETCH, "true"); + + InitialContextFactory factory = new PropertiesFileInitialContextFactory(); + + Hashtable env = new Hashtable(); + + env.put("connectionfactory.connection", "amqp://guest:guest@MLT_ID/test?brokerlist='vm://:1'"); + env.put("queue.queue", "direct://amq.direct//ResetMessageListenerTest"); + + _context = factory.getInitialContext(env); + + Queue queue = (Queue) _context.lookup("queue"); + + // Create Client 1 + _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _consumer1 = _clientSession.createConsumer(queue); + + // Create Client 2 on same session + _consumer2 = _clientSession.createConsumer(queue); + + // Create Producer + _producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection(); + + _producerConnection.start(); + + _producerSession = _producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _producer = _producerSession.createProducer(queue); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + _producer.send(_producerSession.createTextMessage("Message " + msg)); + } + + } + + protected void tearDown() throws Exception + { + _clientConnection.close(); + _producerConnection.close(); + + super.tearDown(); + if (oldImmediatePrefetch == null) + { + oldImmediatePrefetch = AMQSession.IMMEDIATE_PREFETCH_DEFAULT; + } + System.setProperty(AMQSession.IMMEDIATE_PREFETCH, oldImmediatePrefetch); + TransportConnection.killAllVMBrokers(); + } + + public void testAsynchronousRecieve() + { + + _logger.info("Test Start"); + + // Set default Message Listener + try + { + _consumer1.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 1 ML 1 Received Message(" + receivedCount1ML1 + "):" + message); + + receivedCount1ML1++; + if (receivedCount1ML1 == (MSG_COUNT / 2)) + { + _allFirstMessagesSent.countDown(); + } + } + }); + } + catch (JMSException e) + { + _logger.error("Error Setting Default ML on consumer1"); + } + + try + { + _consumer2.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 2 Received Message(" + receivedCount2 + "):" + message); + + receivedCount2++; + if (receivedCount2 == (MSG_COUNT / 2)) + { + _logger.info("Client 2 received all its messages1"); + _allFirstMessagesSent.countDown(); + } + + if (receivedCount2 == MSG_COUNT) + { + _logger.info("Client 2 received all its messages2"); + _allSecondMessagesSent.countDown(); + } + } + }); + + _clientConnection.start(); + } + catch (JMSException e) + { + _logger.error("Error Setting Default ML on consumer2"); + + } + + try + { + _allFirstMessagesSent.await(1000, TimeUnit.MILLISECONDS); + _logger.info("Received first batch of messages"); + } + catch (InterruptedException e) + { + // do nothing + } + + try + { + _clientConnection.stop(); + } + catch (JMSException e) + { + _logger.error("Error stopping connection"); + } + + _logger.info("Reset Message Listener to better listener while connection stopped, will restart session"); + try + { + _consumer1.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _logger.info("Client 1 ML2 Received Message(" + receivedCount1ML1 + "):" + message); + + receivedCount1ML2++; + if (receivedCount1ML2 == (MSG_COUNT / 2)) + { + _allSecondMessagesSent.countDown(); + } + } + }); + + _clientConnection.start(); + } + catch (javax.jms.IllegalStateException e) + { + _logger.error("Connection not stopped while setting ML", e); + fail("Unable to change message listener:" + e.getCause()); + } + catch (JMSException e) + { + _logger.error("Error Setting Better ML on consumer1", e); + } + + try + { + _logger.info("Send additional messages"); + + for (int msg = 0; msg < MSG_COUNT; msg++) + { + _producer.send(_producerSession.createTextMessage("Message " + msg)); + } + } + catch (JMSException e) + { + _logger.error("Unable to send additional messages", e); + } + + _logger.info("Waiting upto 2 seconds for messages"); + + try + { + _allSecondMessagesSent.await(5000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + // do nothing + } + assertEquals("First batch of messages not received correctly", 0, _allFirstMessagesSent.getCount()); + assertEquals("Second batch of messages not received correctly", 0, _allSecondMessagesSent.getCount()); + assertEquals("Client 1 ML1 didn't get all messages", MSG_COUNT / 2, receivedCount1ML1); + assertEquals("Client 2 didn't get all messages", MSG_COUNT, receivedCount2); + assertEquals("Client 1 ML2 didn't get all messages", MSG_COUNT / 2, receivedCount1ML2); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ResetMessageListenerTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java b/Final2/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java new file mode 100644 index 0000000000..4cffcecf8a --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/SpecificMethodFrameListenerTest.java @@ -0,0 +1,71 @@ +package org.apache.qpid.framing; + +import junit.framework.TestCase; +import org.apache.qpid.client.state.listener.SpecificMethodFrameListener; +import org.apache.mina.common.ByteBuffer; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 SpecificMethodFrameListenerTest extends TestCase +{ + + SpecificMethodFrameListener close1a = new SpecificMethodFrameListener(1, ChannelCloseOkBody.class); + SpecificMethodFrameListener close1b = new SpecificMethodFrameListener(1, ChannelCloseOkBody.class); + SpecificMethodFrameListener close2 = new SpecificMethodFrameListener(2, ChannelCloseOkBody.class); + SpecificMethodFrameListener open1a = new SpecificMethodFrameListener(1, ChannelOpenOkBody.class); + SpecificMethodFrameListener open1b = new SpecificMethodFrameListener(1, ChannelOpenOkBody.class); + + public void testEquals() + { + //Check that the the same objects are equal + assertEquals("ChannelCloseOKBody a should equal a", close1a, close1a); + assertEquals("ChannelOpenOkBody a should equal a", open1a, open1a); + + //check that the same values in differnt objects are equal + assertEquals("ChannelCloseOKBody b should equal a", close1b, close1a); + assertEquals("ChannelCloseOKBody a should equal b", close1a, close1b); + assertEquals("ChannelOpenOkBody a should equal b", open1a, open1b); + assertEquals("ChannelOpenOkBody a should equal b", open1a, open1b); + + //Chec that different values fail + //Different channels + assertFalse("ChannelCloseOKBody channel 1 should NOT equal channel 2", close1a.equals(close2)); + assertFalse("ChannelCloseOKBody channel 1 should NOT equal channel 2", close2.equals(close1a)); + + //Different Bodies + assertFalse("ChannelCloseOKBody should not equal ChannelOpenOkBody", close1a.equals(open1a)); + assertFalse("ChannelOpenOkBody should not equal ChannelCloseOKBody", open1a.equals(close1a)); + } + + public void testProcessMethod() throws AMQFrameDecodingException + { + ChannelCloseOkBody ccob = (ChannelCloseOkBody) ChannelCloseOkBody.getFactory().newInstance((byte) 8, (byte) 0, ByteBuffer.allocate(0), 0); + ChannelOpenOkBody coob = (ChannelOpenOkBody) ChannelOpenOkBody.getFactory().newInstance((byte) 8, (byte) 0, ByteBuffer.allocate(0), 0); + + assertTrue("This SpecificMethodFrameListener should process a ChannelCloseOkBody", close1a.processMethod(1, ccob)); + assertFalse("This SpecificMethodFrameListener should NOT process a ChannelOpenOkBody", close1a.processMethod(1, coob)); + + + + + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java b/Final2/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.java new file mode 100644 index 0000000000..60a26c8e62 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/client/message/NonQpidObjectMessage.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.client.message; + +import java.io.Serializable; +import java.util.Enumeration; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.ObjectMessage; + +public class NonQpidObjectMessage implements ObjectMessage { + + private JMSObjectMessage _realMessage; + private String _contentString; + + /** + * Allows us to construct a JMS message which + * does not inherit from the Qpid message superclasses + * and expand our unit testing of MessageConverter et al + */ + public NonQpidObjectMessage() + { + _realMessage = new JMSObjectMessage(); + } + + public String getJMSMessageID() throws JMSException { + return _realMessage.getJMSMessageID(); + } + + public void setJMSMessageID(String string) throws JMSException { + _realMessage.setJMSMessageID(string); + } + + public long getJMSTimestamp() throws JMSException { + return _realMessage.getJMSTimestamp(); + } + + public void setJMSTimestamp(long l) throws JMSException { + _realMessage.setJMSTimestamp(l); + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException { + return _realMessage.getJMSCorrelationIDAsBytes(); + } + + public void setJMSCorrelationIDAsBytes(byte[] bytes) throws JMSException { + _realMessage.setJMSCorrelationIDAsBytes(bytes); + } + + public void setJMSCorrelationID(String string) throws JMSException { + _realMessage.setJMSCorrelationID(string); + } + + public String getJMSCorrelationID() throws JMSException { + return _realMessage.getJMSCorrelationID(); + } + + public Destination getJMSReplyTo() throws JMSException { + return _realMessage.getJMSReplyTo(); + } + + public void setJMSReplyTo(Destination destination) throws JMSException { + _realMessage.setJMSReplyTo(destination); + } + + public Destination getJMSDestination() throws JMSException { + return _realMessage.getJMSDestination(); + } + + public void setJMSDestination(Destination destination) throws JMSException { + _realMessage.setJMSDestination(destination); + } + + public int getJMSDeliveryMode() throws JMSException { + return _realMessage.getJMSDeliveryMode(); + } + + public void setJMSDeliveryMode(int i) throws JMSException { + _realMessage.setJMSDeliveryMode(i); + } + + public boolean getJMSRedelivered() throws JMSException { + return _realMessage.getJMSRedelivered(); + } + + public void setJMSRedelivered(boolean b) throws JMSException { + _realMessage.setJMSRedelivered(b); + } + + public String getJMSType() throws JMSException { + return _realMessage.getJMSType(); + } + + public void setJMSType(String string) throws JMSException { + _realMessage.setJMSType(string); + } + + public long getJMSExpiration() throws JMSException { + return _realMessage.getJMSExpiration(); + } + + public void setJMSExpiration(long l) throws JMSException { + _realMessage.setJMSExpiration(l); + } + + public int getJMSPriority() throws JMSException { + return _realMessage.getJMSPriority(); + } + + public void setJMSPriority(int i) throws JMSException { + _realMessage.setJMSPriority(i); + } + + public void clearProperties() throws JMSException { + _realMessage.clearProperties(); + } + + public boolean propertyExists(String string) throws JMSException { + return _realMessage.propertyExists(string); + } + + public boolean getBooleanProperty(String string) throws JMSException { + return _realMessage.getBooleanProperty(string); + } + + public byte getByteProperty(String string) throws JMSException { + return _realMessage.getByteProperty(string); + } + + public short getShortProperty(String string) throws JMSException { + return _realMessage.getShortProperty(string); + } + + public int getIntProperty(String string) throws JMSException { + return _realMessage.getIntProperty(string); + } + + public long getLongProperty(String string) throws JMSException { + return _realMessage.getLongProperty(string); + } + + public float getFloatProperty(String string) throws JMSException { + return _realMessage.getFloatProperty(string); + } + + public double getDoubleProperty(String string) throws JMSException { + return _realMessage.getDoubleProperty(string); + } + + public String getStringProperty(String string) throws JMSException { + return _realMessage.getStringProperty(string); + } + + public Object getObjectProperty(String string) throws JMSException { + return _realMessage.getObjectProperty(string); + } + + public Enumeration getPropertyNames() throws JMSException { + return _realMessage.getPropertyNames(); + } + + public void setBooleanProperty(String string, boolean b) throws JMSException { + _realMessage.setBooleanProperty(string,b); + } + + public void setByteProperty(String string, byte b) throws JMSException { + _realMessage.setByteProperty(string,b); + } + + public void setShortProperty(String string, short i) throws JMSException { + _realMessage.setShortProperty(string,i); + } + + public void setIntProperty(String string, int i) throws JMSException { + _realMessage.setIntProperty(string,i); + } + + public void setLongProperty(String string, long l) throws JMSException { + _realMessage.setLongProperty(string,l); + } + + public void setFloatProperty(String string, float v) throws JMSException { + _realMessage.setFloatProperty(string,v); + } + + public void setDoubleProperty(String string, double v) throws JMSException { + _realMessage.setDoubleProperty(string,v); + } + + public void setStringProperty(String string, String string1) throws JMSException { + _realMessage.setStringProperty(string,string1); + } + + public void setObjectProperty(String string, Object object) throws JMSException { + _realMessage.setObjectProperty(string,object); + } + + public void acknowledge() throws JMSException { + _realMessage.acknowledge(); + } + + public void clearBody() throws JMSException { + _realMessage.clearBody(); + } + + public void setObject(Serializable serializable) throws JMSException { + if (serializable instanceof String) + { + _contentString = (String)serializable; + } + } + + public Serializable getObject() throws JMSException { + return _contentString; } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java b/Final2/java/client/src/test/java/org/apache/qpid/client/message/TestMessageHelper.java new file mode 100644 index 0000000000..9b477c19e2 --- /dev/null +++ b/Final2/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(); + } + + public static JMSBytesMessage newJMSBytesMessage() throws JMSException + { + return new JMSBytesMessage(); + } + + public static JMSMapMessage newJMSMapMessage() throws JMSException + { + return new JMSMapMessage(); + } + + public static JMSStreamMessage newJMSStreamMessage() + { + return new JMSStreamMessage(); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java new file mode 100644 index 0000000000..b6f46b4acc --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.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.test.unit.ack; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; +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.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.TextMessage; + +import java.util.concurrent.atomic.AtomicInteger; + +public class RecoverTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(RecoverTest.class); + + private Exception _error; + private AtomicInteger count; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + _error = null; + count = new AtomicInteger(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + count = null; + } + + public void testRecoverResendsMsgs() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + + Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"), + new AMQShortString("someQ"), false, true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + // force synch to ensure the consumer has resulted in a bound queue + // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); + // This is the default now + + AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + _logger.info("Sending four messages"); + producer.send(producerSession.createTextMessage("msg1")); + producer.send(producerSession.createTextMessage("msg2")); + producer.send(producerSession.createTextMessage("msg3")); + producer.send(producerSession.createTextMessage("msg4")); + + con2.close(); + + _logger.info("Starting connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(); + tm.acknowledge(); + _logger.info("Received and acknowledged first message"); + consumer.receive(); + consumer.receive(); + consumer.receive(); + _logger.info("Received all four messages. Calling recover with three outstanding messages"); + // no ack for last three messages so when I call recover I expect to get three messages back + consumerSession.recover(); + tm = (TextMessage) consumer.receive(3000); + assertEquals("msg2", tm.getText()); + + tm = (TextMessage) consumer.receive(3000); + assertEquals("msg3", tm.getText()); + + tm = (TextMessage) consumer.receive(3000); + assertEquals("msg4", tm.getText()); + + _logger.info("Received redelivery of three messages. Acknowledging last message"); + tm.acknowledge(); + + _logger.info("Calling acknowledge with no outstanding messages"); + // all acked so no messages to be delivered + consumerSession.recover(); + + tm = (TextMessage) consumer.receiveNoWait(); + assertNull(tm); + _logger.info("No messages redelivered as is expected"); + + con.close(); + } + + public void testRecoverResendsMsgsAckOnEarlier() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + + Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("someQ"), + new AMQShortString("someQ"), false, true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + // force synch to ensure the consumer has resulted in a bound queue + // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS); + // This is the default now + + AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + _logger.info("Sending four messages"); + producer.send(producerSession.createTextMessage("msg1")); + producer.send(producerSession.createTextMessage("msg2")); + producer.send(producerSession.createTextMessage("msg3")); + producer.send(producerSession.createTextMessage("msg4")); + + con2.close(); + + _logger.info("Starting connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(); + consumer.receive(); + tm.acknowledge(); + _logger.info("Received 2 messages, acknowledge() first message, should acknowledge both"); + + consumer.receive(); + consumer.receive(); + _logger.info("Received all four messages. Calling recover with two outstanding messages"); + // no ack for last three messages so when I call recover I expect to get three messages back + consumerSession.recover(); + TextMessage tm3 = (TextMessage) consumer.receive(3000); + assertEquals("msg3", tm3.getText()); + + TextMessage tm4 = (TextMessage) consumer.receive(3000); + assertEquals("msg4", tm4.getText()); + + _logger.info("Received redelivery of two messages. calling acknolwedgeThis() first of those message"); + ((org.apache.qpid.jms.Message) tm3).acknowledgeThis(); + + _logger.info("Calling recover"); + // all acked so no messages to be delivered + consumerSession.recover(); + + tm4 = (TextMessage) consumer.receive(3000); + assertEquals("msg4", tm4.getText()); + ((org.apache.qpid.jms.Message) tm4).acknowledgeThis(); + + _logger.info("Calling recover"); + // all acked so no messages to be delivered + consumerSession.recover(); + + tm = (TextMessage) consumer.receiveNoWait(); + assertNull(tm); + _logger.info("No messages redelivered as is expected"); + + con.close(); + } + + public void testAcknowledgePerConsumer() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + + Session consumerSession = con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"), + false, true); + Queue queue2 = + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q2"), new AMQShortString("Q2"), + false, true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + MessageConsumer consumer2 = consumerSession.createConsumer(queue2); + + AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + MessageProducer producer2 = producerSession.createProducer(queue2); + + producer.send(producerSession.createTextMessage("msg1")); + producer2.send(producerSession.createTextMessage("msg2")); + + con2.close(); + + _logger.info("Starting connection"); + con.start(); + + TextMessage tm2 = (TextMessage) consumer2.receive(); + assertNotNull(tm2); + assertEquals("msg2", tm2.getText()); + + tm2.acknowledge(); + + consumerSession.recover(); + + TextMessage tm1 = (TextMessage) consumer.receive(2000); + assertNotNull(tm1); + assertEquals("msg1", tm1.getText()); + + con.close(); + + } + + public void testRecoverInAutoAckListener() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + + final Session consumerSession = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = + new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), new AMQShortString("Q3"), + false, true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + MessageProducer producer = consumerSession.createProducer(queue); + producer.send(consumerSession.createTextMessage("hello")); + + final Object lock = new Object(); + + consumer.setMessageListener(new MessageListener() + { + + public void onMessage(Message message) + { + try + { + count.incrementAndGet(); + if (count.get() == 1) + { + if (message.getJMSRedelivered()) + { + setError( + new Exception("Message marked as redilvered on what should be first delivery attempt")); + } + + consumerSession.recover(); + } + else if (count.get() == 2) + { + if (!message.getJMSRedelivered()) + { + setError( + new Exception( + "Message not marked as redilvered on what should be second delivery attempt")); + } + } + else + { + System.err.println(message); + fail("Message delivered too many times!: " + count); + } + } + catch (JMSException e) + { + _logger.error("Error recovering session: " + e, e); + setError(e); + } + + synchronized (lock) + { + lock.notify(); + } + } + }); + + con.start(); + + long waitTime = 300000L; + long waitUntilTime = System.currentTimeMillis() + waitTime; + + synchronized (lock) + { + while ((count.get() <= 1) && (waitTime > 0)) + { + lock.wait(waitTime); + if (count.get() <= 1) + { + waitTime = waitUntilTime - System.currentTimeMillis(); + } + } + } + + Thread.sleep(1000); + + if (count.get() != 2) + { + System.err.println("Count != 2 : " + count); + } + + assertTrue(count.get() == 2); + + con.close(); + + if (_error != null) + { + throw _error; + } + } + + private void setError(Exception e) + { + _error = e; + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(RecoverTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java new file mode 100644 index 0000000000..da1b46ee2c --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/BytesMessageTest.java @@ -0,0 +1,288 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.mina.common.ByteBuffer; + +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.client.message.JMSBytesMessage; +import org.apache.qpid.testutil.VMBrokerSetup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class BytesMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(BytesMessageTest.class); + + private Connection _connection; + private Destination _destination; + private Session _session; + private final List received = new ArrayList(); + private final List messages = new ArrayList(); + private int _count = 100; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + void init(AMQConnection connection) throws Exception + { + init(connection, new AMQQueue(connection, randomize("BytesMessageTest"), true)); + } + + void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + // Set up a slow consumer. + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + } + + public void test() throws Exception + { + try + { + send(_count); + waitFor(_count); + check(); + _logger.info("Completed without failure"); + } + finally + { + _connection.close(); + } + } + + void send(int count) throws JMSException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + BytesMessage msg = _session.createBytesMessage(); + + try + { + msg.readFloat(); + Assert.fail("Message should not be readable"); + } + catch (MessageNotReadableException mnwe) + { + // normal execution + } + + byte[] data = ("Message " + i).getBytes(); + msg.writeBytes(data); + messages.add(data); + producer.send(msg); + } + } + + void waitFor(int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + received.wait(); + } + } + } + + void check() throws JMSException + { + List actual = new ArrayList(); + for (JMSBytesMessage m : received) + { + ByteBuffer buffer = m.getData(); + byte[] data = new byte[buffer.remaining()]; + buffer.get(data); + actual.add(data); + + // Check Body Write Status + try + { + m.writeBoolean(true); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearBody(); + + try + { + m.writeBoolean(true); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + // Check property write status + try + { + m.setStringProperty("test", "test"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearProperties(); + + try + { + m.setStringProperty("test", "test"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + } + + assertEqual(messages.iterator(), actual.iterator()); + } + + private static void assertEqual(Iterator expected, Iterator actual) + { + List errors = new ArrayList(); + while (expected.hasNext() && actual.hasNext()) + { + try + { + assertEquivalent((byte[]) expected.next(), (byte[]) actual.next()); + } + catch (Exception e) + { + errors.add(e.getMessage()); + } + } + while (expected.hasNext()) + { + errors.add("Expected " + expected.next() + " but no more actual values."); + } + while (actual.hasNext()) + { + errors.add("Found " + actual.next() + " but no more expected values."); + } + + if (!errors.isEmpty()) + { + throw new RuntimeException(errors.toString()); + } + } + + private static void assertEquivalent(byte[] expected, byte[] actual) + { + if (expected.length != actual.length) + { + throw new RuntimeException("Expected length " + expected.length + " got " + actual.length); + } + + for (int i = 0; i < expected.length; i++) + { + if (expected[i] != actual[i]) + { + throw new RuntimeException("Failed on byte " + i + " of " + expected.length); + } + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + received.add((JMSBytesMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + final String connectionString; + final int count; + if (argv.length == 0) + { + connectionString = "vm://:1"; + count = 100; + } + else + { + connectionString = argv[0]; + count = Integer.parseInt(argv[1]); + } + + System.out.println("connectionString = " + connectionString); + System.out.println("count = " + count); + + BytesMessageTest test = new BytesMessageTest(); + test._connectionString = connectionString; + test._count = count; + test.test(); + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(BytesMessageTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableKeyEnumeratorTest.java new file mode 100644 index 0000000000..ddbc69826d --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.java new file mode 100644 index 0000000000..aff496becf --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTableMessageTest.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.test.unit.basic; + +import junit.framework.TestCase; + +import org.apache.mina.common.ByteBuffer; + +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.client.message.JMSBytesMessage; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.FieldTableFactory; +import org.apache.qpid.testutil.VMBrokerSetup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; + +import java.io.IOException; +import java.util.ArrayList; + +public class FieldTableMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(FieldTableMessageTest.class); + + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private final ArrayList received = new ArrayList(); + private FieldTable _expected; + private int _count = 10; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + private void init(AMQConnection connection) throws Exception + { + init(connection, new AMQQueue(connection, randomize("FieldTableMessageTest"), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + // set up a slow consumer + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + + // _expected = new FieldTableTest().load("FieldTableTest2.properties"); + _expected = load(); + } + + private FieldTable load() throws IOException + { + FieldTable result = FieldTableFactory.newFieldTable(); + result.setLong("one", 1L); + result.setLong("two", 2L); + result.setLong("three", 3L); + result.setLong("four", 4L); + result.setLong("five", 5L); + + return result; + } + + public void test() throws Exception + { + int count = _count; + send(count); + waitFor(count); + check(); + _logger.info("Completed without failure"); + _connection.close(); + } + + void send(int count) throws JMSException, IOException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + BytesMessage msg = _session.createBytesMessage(); + msg.writeBytes(_expected.getDataAsBytes()); + producer.send(msg); + } + } + + void waitFor(int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + received.wait(); + } + } + } + + void check() throws JMSException, AMQFrameDecodingException + { + for (Object m : received) + { + ByteBuffer buffer = ((JMSBytesMessage) m).getData(); + FieldTable actual = FieldTableFactory.newFieldTable(buffer, buffer.remaining()); + for (String key : _expected.keys()) + { + assertEquals("Values for " + key + " did not match", _expected.getObject(key), actual.getObject(key)); + } + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + received.add((JMSBytesMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + FieldTableMessageTest test = new FieldTableMessageTest(); + test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0]; + test.setUp(); + test._count = (argv.length > 1) ? Integer.parseInt(argv[1]) : 5; + test.test(); + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(FieldTableMessageTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/FieldTablePropertyTest.java new file mode 100644 index 0000000000..60ed688897 --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java new file mode 100644 index 0000000000..83846e0081 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/InvalidDestinationTest.java @@ -0,0 +1,130 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; +import org.apache.qpid.client.transport.TransportConnection; + +import junit.framework.TestCase; + +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.QueueSession; +import javax.jms.Queue; +import javax.jms.QueueSender; +import javax.jms.TextMessage; +import javax.jms.InvalidDestinationException; + +public class InvalidDestinationTest extends TestCase +{ + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private MessageConsumer _consumer; + + private static final String VM_BROKER = "vm://:1"; + + + protected void setUp() throws Exception + { + super.setUp(); + createVMBroker(); + _connection = new AMQConnection(VM_BROKER, "guest", "guest", "ReceiveTestClient", "test"); + } + + public void createVMBroker() + { + try + { + TransportConnection.createVMBroker(1); + } + catch (AMQVMBrokerCreationException e) + { + fail("Unable to create broker: " + e); + } + } + + protected void tearDown() throws Exception + { + _connection.close(); + TransportConnection.killVMBroker(1); + super.tearDown(); + } + + + + public void testInvalidDestination() throws Exception + { + Queue invalidDestination = new AMQQueue("amq.direct","unknownQ"); + AMQQueue validDestination = new AMQQueue("amq.direct","knownQ"); + QueueSession queueSession = _connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + // This is the only easy way to create and bind a queue from the API :-( + MessageConsumer consumer = queueSession.createConsumer(validDestination); + + QueueSender sender = queueSession.createSender(invalidDestination); + TextMessage msg = queueSession.createTextMessage("Hello"); + try + { + sender.send(msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.close(); + + sender = queueSession.createSender(null); + invalidDestination = new AMQQueue("amq.direct","unknownQ"); + + try + { + sender.send(invalidDestination,msg); + fail("Expected InvalidDestinationException"); + } + catch (InvalidDestinationException ex) + { + // pass + } + sender.send(validDestination,msg); + sender.close(); + validDestination = new AMQQueue("amq.direct","knownQ"); + sender = queueSession.createSender(validDestination); + sender.send(msg); + + + + + } + + + public static junit.framework.Test suite() + { + + return new junit.framework.TestSuite(InvalidDestinationTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java new file mode 100644 index 0000000000..03698b2ab2 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/LargeMessageTest.java @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.ArrayList; +import java.util.List; + +public class LargeMessageTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(LargeMessageTest.class); + + private AMQConnection _connection; + private Destination _destination; + private AMQSession _session; + private final List received = new ArrayList(); + public String _broker = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + try + { + init(new AMQConnection(_broker, "guest", "guest", "LargeMessageTest", "test")); + } + catch (Exception e) + { + fail("Unable to initialilse connection: " + e); + } + } + + protected void tearDown() throws Exception + { + super.tearDown(); + _session.close(); + _connection.close(); + TransportConnection.killAllVMBrokers(); + } + + private void init(AMQConnection connection) throws Exception + { + Destination destination = new AMQQueue(connection, "LargeMessageTest", true); + init(connection, destination); + } + + private void init(AMQConnection connection, Destination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + connection.start(); + } + + // Test boundary of 1 packet to 2 packets + public void test64kminus1() + { + checkLargeMessage((64 * 1024) - 1); + } + + public void test64k() + { + checkLargeMessage(64 * 1024); + } + + public void test64kplus1() + { + checkLargeMessage((64 * 1024) + 1); + } + + // Test packet boundary of 3 packtes + public void test128kminus1() + { + checkLargeMessage((128 * 1024) - 1); + } + + public void test128k() + { + checkLargeMessage(128 * 1024); + } + + public void test128kplus1() + { + checkLargeMessage((128 * 1024) + 1); + } + + // Testing larger messages + + public void test256k() + { + checkLargeMessage(256 * 1024); + } + + public void test512k() + { + checkLargeMessage(512 * 1024); + } + + public void test1024k() + { + checkLargeMessage(1024 * 1024); + } + + protected void checkLargeMessage(int messageSize) + { + try + { + MessageConsumer consumer = _session.createConsumer(_destination); + MessageProducer producer = _session.createProducer(_destination); + _logger.info("Testing message of size:" + messageSize); + + String _messageText = buildLargeMessage(messageSize); + + _logger.debug("Message built"); + + producer.send(_session.createTextMessage(_messageText)); + + TextMessage result = (TextMessage) consumer.receive(10000); + + assertNotNull("Null message recevied", result); + assertEquals("Message Size", _messageText.length(), result.getText().length()); + assertEquals("Message content differes", _messageText, result.getText()); + } + catch (JMSException e) + { + e.printStackTrace(); + fail("Excpetion occured:" + e.getCause()); + } + } + + private String buildLargeMessage(int size) + { + StringBuilder builder = new StringBuilder(size); + + char ch = 'a'; + + for (int i = 1; i <= size; i++) + { + builder.append(ch); + + if ((i % 1000) == 0) + { + ch++; + if (ch == ('z' + 1)) + { + ch = 'a'; + } + } + } + + return builder.toString(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(LargeMessageTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java new file mode 100644 index 0000000000..6708fefa86 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MapMessageTest.java @@ -0,0 +1,1277 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageFormatException; +import javax.jms.MessageListener; +import javax.jms.MessageNotWriteableException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class MapMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(MapMessageTest.class); + + private AMQConnection _connection; + private Destination _destination; + private AMQSession _session; + private final List received = new ArrayList(); + + private static final String MESSAGE = "Message "; + private int _count = 100; + public String _connectionString = "vm://:1"; + private byte[] _bytes = { 99, 98, 97, 96, 95 }; + private static final float _smallfloat = 100.0f; + + protected void setUp() throws Exception + { + super.setUp(); + try + { + TransportConnection.createVMBroker(1); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + catch (Exception e) + { + fail("Unable to initialilse connection: " + e); + } + } + + protected void tearDown() throws Exception + { + _logger.info("Tearing Down unit.basic.MapMessageTest"); + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + private void init(AMQConnection connection) throws Exception + { + Destination destination = new AMQQueue(connection, randomize("MapMessageTest"), true); + init(connection, destination); + } + + private void init(AMQConnection connection, Destination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // set up a slow consumer + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + } + + public void test() throws Exception + { + int count = _count; + send(count); + waitFor(count); + check(); + _connection.close(); + } + + void send(int count) throws JMSException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + MapMessage message = _session.createMapMessage(); + + setMapValues(message, i); + + producer.send(message); + } + } + + private void setMapValues(MapMessage message, int i) throws JMSException + { + message.setBoolean("odd", (i / 2) == 0); + message.setByte("byte", (byte) Byte.MAX_VALUE); + message.setBytes("bytes", _bytes); + message.setChar("char", (char) 'c'); + message.setDouble("double", (double) Double.MAX_VALUE); + message.setFloat("float", (float) Float.MAX_VALUE); + message.setFloat("smallfloat", 100); + message.setInt("messageNumber", i); + message.setInt("int", (int) Integer.MAX_VALUE); + message.setLong("long", (long) Long.MAX_VALUE); + message.setShort("short", (short) Short.MAX_VALUE); + message.setString("message", MESSAGE + i); + + // Test Setting Object Values + message.setObject("object-bool", true); + message.setObject("object-byte", Byte.MAX_VALUE); + message.setObject("object-bytes", _bytes); + message.setObject("object-char", 'c'); + message.setObject("object-double", Double.MAX_VALUE); + message.setObject("object-float", Float.MAX_VALUE); + message.setObject("object-int", Integer.MAX_VALUE); + message.setObject("object-long", Long.MAX_VALUE); + message.setObject("object-short", Short.MAX_VALUE); + + // Set a null String value + message.setString("nullString", null); + // Highlight protocol problem + message.setString("emptyString", ""); + + } + + void waitFor(int count) throws Exception + { + long waitTime = 30000L; + long waitUntilTime = System.currentTimeMillis() + 30000L; + + synchronized (received) + { + while ((received.size() < count) && (waitTime > 0)) + { + if (received.size() < count) + { + received.wait(waitTime); + } + + if (received.size() < count) + { + waitTime = waitUntilTime - System.currentTimeMillis(); + } + } + + if (received.size() < count) + { + throw new Exception("Timed-out. Waiting for " + count + " only got " + received.size()); + } + } + } + + void check() throws JMSException + { + List actual = new ArrayList(); + int count = 0; + for (JMSMapMessage m : received) + { + actual.add(m.getString("message")); + + testMapValues(m, count); + + testCorrectExceptions(m); + + testMessageWriteStatus(m); + + testPropertyWriteStatus(m); + + count++; + } + } + + private void testCorrectExceptions(JMSMapMessage m) throws JMSException + { + testBoolean(m); + + testByte(m); + + testBytes(m); + + testChar(m); + + testDouble(m); + + testFloat(m); + + testInt(m); + + testLong(m); + + testShort(m); + + testString(m); + } + + private void testString(JMSMapMessage m) throws JMSException + { + + Assert.assertFalse(m.getBoolean("message")); + + try + { + m.getByte("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("message"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + try + { + m.getInt("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + + try + { + m.getLong("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getFloat("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("message"); + fail("Exception Expected."); + } + catch (NumberFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("message"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(MESSAGE + m.getInt("messageNumber"), m.getString("message")); + } + + private void testShort(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("short"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("short"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(Short.MAX_VALUE, m.getShort("short")); + + // Try bad reads + try + { + m.getChar("short"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + Assert.assertEquals(Short.MAX_VALUE, m.getInt("short")); + + Assert.assertEquals(Short.MAX_VALUE, m.getLong("short")); + + // Try bad reads + try + { + m.getFloat("short"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("short"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("short"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Short.MAX_VALUE, m.getString("short")); + } + + private void testLong(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("long"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + try + { + m.getInt("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(Long.MAX_VALUE, m.getLong("long")); + + // Try bad reads + try + { + m.getFloat("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("long"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Long.MAX_VALUE, m.getString("long")); + } + + private void testDouble(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("double"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + try + { + m.getInt("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getLong("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getFloat("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(Double.MAX_VALUE, m.getDouble("double")); + + // Try bad reads + try + { + m.getBytes("double"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Double.MAX_VALUE, m.getString("double")); + } + + private void testFloat(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("float"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + try + { + m.getInt("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getLong("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(Float.MAX_VALUE, m.getFloat("float")); + + Assert.assertEquals(_smallfloat, (float) m.getDouble("smallfloat")); + + // Try bad reads + try + { + m.getBytes("float"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Float.MAX_VALUE, m.getString("float")); + } + + private void testInt(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("int"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + Assert.assertEquals(Integer.MAX_VALUE, m.getInt("int")); + + Assert.assertEquals(Integer.MAX_VALUE, (int) m.getLong("int")); + + // Try bad reads + try + { + m.getFloat("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("int"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Integer.MAX_VALUE, m.getString("int")); + } + + private void testChar(JMSMapMessage m) throws JMSException + { + + // Try bad reads + try + { + m.getBoolean("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals('c', m.getChar("char")); + + try + { + m.getInt("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getLong("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getFloat("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("char"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + 'c', m.getString("char")); + } + + private void testBytes(JMSMapMessage m) throws JMSException + { + // Try bad reads + try + { + m.getBoolean("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getByte("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getShort("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getChar("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + try + { + m.getInt("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + try + { + m.getLong("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getFloat("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + assertBytesEqual(_bytes, m.getBytes("bytes")); + + try + { + m.getString("bytes"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + } + + private void testByte(JMSMapMessage m) throws JMSException + { + // Try bad reads + try + { + m.getBoolean("byte"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals(Byte.MAX_VALUE, m.getByte("byte")); + + Assert.assertEquals((short) Byte.MAX_VALUE, m.getShort("byte")); + + // Try bad reads + try + { + m.getChar("byte"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + + // Reading a byte as an int is ok + Assert.assertEquals((short) Byte.MAX_VALUE, m.getInt("byte")); + + Assert.assertEquals((short) Byte.MAX_VALUE, m.getLong("byte")); + + // Try bad reads + try + { + m.getFloat("byte"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("byte"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("byte"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + Byte.MAX_VALUE, m.getString("byte")); + + } + + private void testBoolean(JMSMapMessage m) throws JMSException + { + + Assert.assertEquals((m.getInt("messageNumber") / 2) == 0, m.getBoolean("odd")); + + // Try bad reads + try + { + m.getByte("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + // Try bad reads + try + { + m.getShort("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getChar("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException npe) + { + // normal execution + } + // Try bad reads + try + { + m.getInt("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getLong("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getFloat("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getDouble("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + // Try bad reads + try + { + m.getBytes("odd"); + fail("Exception Expected."); + } + catch (MessageFormatException nfe) + { + // normal execution + } + + Assert.assertEquals("" + ((m.getInt("messageNumber") / 2) == 0), m.getString("odd")); + } + + private void testPropertyWriteStatus(JMSMapMessage m) throws JMSException + { + // Check property write status + try + { + m.setStringProperty("test", "test"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearProperties(); + + try + { + m.setStringProperty("test", "test"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + } + + private void testMessageWriteStatus(JMSMapMessage m) throws JMSException + { + try + { + m.setInt("testint", 3); + fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearBody(); + + try + { + m.setInt("testint", 3); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + } + + private void testMapValues(JMSMapMessage m, int count) throws JMSException + { + // Test get + + // Boolean + assertEqual((count / 2) == 0, m.getBoolean("odd")); + assertEqual("" + ((count / 2) == 0), m.getString("odd")); + + // Byte + assertEqual(Byte.MAX_VALUE, m.getByte("byte")); + assertEqual("" + Byte.MAX_VALUE, m.getString("byte")); + + // Bytes + assertBytesEqual(_bytes, m.getBytes("bytes")); + + // Char + assertEqual('c', m.getChar("char")); + + // Double + assertEqual(Double.MAX_VALUE, m.getDouble("double")); + assertEqual("" + Double.MAX_VALUE, m.getString("double")); + + // Float + assertEqual(Float.MAX_VALUE, m.getFloat("float")); + assertEqual(_smallfloat, (float) m.getDouble("smallfloat")); + assertEqual("" + Float.MAX_VALUE, m.getString("float")); + + // Integer + assertEqual(Integer.MAX_VALUE, m.getInt("int")); + assertEqual("" + Integer.MAX_VALUE, m.getString("int")); + assertEqual(count, m.getInt("messageNumber")); + + // long + assertEqual(Long.MAX_VALUE, m.getLong("long")); + assertEqual("" + Long.MAX_VALUE, m.getString("long")); + + // Short + assertEqual(Short.MAX_VALUE, m.getShort("short")); + assertEqual("" + Short.MAX_VALUE, m.getString("short")); + assertEqual((int) Short.MAX_VALUE, m.getInt("short")); + + // String + assertEqual(MESSAGE + count, m.getString("message")); + + // Test getObjects + assertEqual(true, m.getObject("object-bool")); + assertEqual(Byte.MAX_VALUE, m.getObject("object-byte")); + assertBytesEqual(_bytes, (byte[]) m.getObject("object-bytes")); + assertEqual('c', m.getObject("object-char")); + assertEqual(Double.MAX_VALUE, m.getObject("object-double")); + assertEqual(Float.MAX_VALUE, m.getObject("object-float")); + assertEqual(Integer.MAX_VALUE, m.getObject("object-int")); + assertEqual(Long.MAX_VALUE, m.getObject("object-long")); + assertEqual(Short.MAX_VALUE, m.getObject("object-short")); + + // Check Special values + assertTrue(m.getString("nullString") == null); + assertEqual("", m.getString("emptyString")); + } + + 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]); + } + } + + private static void assertEqual(Iterator expected, Iterator actual) + { + List errors = new ArrayList(); + while (expected.hasNext() && actual.hasNext()) + { + try + { + assertEqual(expected.next(), actual.next()); + } + catch (Exception e) + { + errors.add(e.getMessage()); + } + } + while (expected.hasNext()) + { + errors.add("Expected " + expected.next() + " but no more actual values."); + } + while (actual.hasNext()) + { + errors.add("Found " + actual.next() + " but no more expected values."); + } + + if (!errors.isEmpty()) + { + throw new RuntimeException(errors.toString()); + } + } + + private static void assertEqual(Object expected, Object actual) + { + if (!expected.equals(actual)) + { + throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'"); + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + _logger.info("****************** Recevied Messgage:" + (JMSMapMessage) message); + received.add((JMSMapMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + MapMessageTest test = new MapMessageTest(); + test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0]; + test.setUp(); + if (argv.length > 1) + { + test._count = Integer.parseInt(argv[1]); + } + + test.test(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MapMessageTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.java new file mode 100644 index 0000000000..65b3d60ad9 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/MultipleConnectionTest.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.test.unit.basic; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.exchange.ExchangeDefaults; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; + +public class MultipleConnectionTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(MultipleConnectionTest.class); + + public static final String _defaultBroker = "vm://:1"; + public String _connectionString = _defaultBroker; + + private static class Receiver + { + private AMQConnection _connection; + private Session[] _sessions; + private MessageCounter[] _counters; + + Receiver(String broker, AMQDestination dest, int sessions) throws Exception + { + this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "test"), dest, sessions); + } + + Receiver(AMQConnection connection, AMQDestination dest, int sessions) throws Exception + { + _connection = connection; + _sessions = new AMQSession[sessions]; + _counters = new MessageCounter[sessions]; + for (int i = 0; i < sessions; i++) + { + _sessions[i] = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _counters[i] = new MessageCounter(_sessions[i].toString()); + _sessions[i].createConsumer(dest).setMessageListener(_counters[i]); + } + + _connection.start(); + } + + void close() throws JMSException + { + _connection.close(); + } + } + + private static class Publisher + { + private AMQConnection _connection; + private Session _session; + private MessageProducer _producer; + + Publisher(String broker, AMQDestination dest) throws Exception + { + this(new AMQConnection(broker, "guest", "guest", randomize("Client"), "test"), dest); + } + + Publisher(AMQConnection connection, AMQDestination dest) throws Exception + { + _connection = connection; + _session = _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _producer = _session.createProducer(dest); + } + + void send(String msg) throws JMSException + { + _producer.send(_session.createTextMessage(msg)); + } + + void close() throws JMSException + { + _connection.close(); + } + } + + private static class MessageCounter implements MessageListener + { + private final String _name; + private int _count; + + MessageCounter(String name) + { + _name = name; + } + + public synchronized void onMessage(Message message) + { + _count++; + notify(); + } + + synchronized boolean waitUntil(int expected, long maxWait) throws InterruptedException + { + long start = System.currentTimeMillis(); + while (expected > _count) + { + long timeLeft = maxWait - timeSince(start); + if (timeLeft < 0) + { + break; + } + + wait(timeLeft); + } + + return expected <= _count; + } + + private long timeSince(long start) + { + return System.currentTimeMillis() - start; + } + + public synchronized String toString() + { + return _name + ": " + _count; + } + } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + private static void waitForCompletion(int expected, long wait, Receiver[] receivers) throws InterruptedException + { + for (int i = 0; i < receivers.length; i++) + { + waitForCompletion(expected, wait, receivers[i]._counters); + } + } + + private static void waitForCompletion(int expected, long wait, MessageCounter[] counters) throws InterruptedException + { + for (int i = 0; i < counters.length; i++) + { + if (!counters[i].waitUntil(expected, wait)) + { + throw new RuntimeException("Expected: " + expected + " got " + counters[i]); + } + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + String broker = (argv.length > 0) ? argv[0] : _defaultBroker; + + MultipleConnectionTest test = new MultipleConnectionTest(); + test._connectionString = broker; + test.test(); + } + + public void test() throws Exception + { + String broker = _connectionString; + int messages = 10; + + AMQTopic topic = new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, "amq.topic"); + + Receiver[] receivers = new Receiver[] { new Receiver(broker, topic, 2), new Receiver(broker, topic, 14) }; + + Publisher publisher = new Publisher(broker, topic); + for (int i = 0; i < messages; i++) + { + publisher.send("Message " + (i + 1)); + } + + try + { + waitForCompletion(messages, 5000, receivers); + _logger.info("All receivers received all expected messages"); + } + finally + { + publisher.close(); + for (int i = 0; i < receivers.length; i++) + { + receivers[i].close(); + } + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(MultipleConnectionTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.java new file mode 100644 index 0000000000..9237555734 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ObjectMessageTest.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.test.unit.basic; + +import junit.framework.Assert; +import junit.framework.TestCase; + +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.client.message.JMSObjectMessage; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageNotWriteableException; +import javax.jms.MessageProducer; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class ObjectMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class); + + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private final List received = new ArrayList(); + private final List messages = new ArrayList(); + private int _count = 100; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + try + { + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + catch (Exception e) + { + fail("Uable to initialise: " + e); + } + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + private void init(AMQConnection connection) throws Exception + { + init(connection, new AMQQueue(connection, randomize("ObjectMessageTest"), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + // set up a slow consumer + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + } + + public void test() throws Exception + { + int count = _count; + send(count); + waitFor(count); + check(); + _logger.info("Completed without failure"); + _connection.close(); + } + + void send(int count) throws JMSException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + Payload payload = new Payload("Message " + i); + messages.add(payload); + producer.send(_session.createObjectMessage(payload)); + } + } + + void waitFor(int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + received.wait(); + } + } + } + + void check() throws JMSException + { + List actual = new ArrayList(); + for (JMSObjectMessage m : received) + { + actual.add(m.getObject()); + + try + { + m.setObject("Test text"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearBody(); + + try + { + m.setObject("Test text"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + // Check property write status + try + { + m.setStringProperty("test", "test"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearProperties(); + + try + { + m.setStringProperty("test", "test"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + } + + assertEqual(messages.iterator(), actual.iterator()); + + } + + private static void assertEqual(Iterator expected, Iterator actual) + { + List errors = new ArrayList(); + while (expected.hasNext() && actual.hasNext()) + { + try + { + assertEqual(expected.next(), actual.next()); + } + catch (Exception e) + { + errors.add(e.getMessage()); + } + } + while (expected.hasNext()) + { + errors.add("Expected " + expected.next() + " but no more actual values."); + } + while (actual.hasNext()) + { + errors.add("Found " + actual.next() + " but no more expected values."); + } + + if (!errors.isEmpty()) + { + throw new RuntimeException(errors.toString()); + } + } + + private static void assertEqual(Object expected, Object actual) + { + if (!expected.equals(actual)) + { + throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'"); + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + received.add((JMSObjectMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + private static class Payload implements Serializable + { + private final String data; + + Payload(String data) + { + this.data = data; + } + + public int hashCode() + { + return data.hashCode(); + } + + public boolean equals(Object o) + { + return (o instanceof Payload) && ((Payload) o).data.equals(data); + } + + public String toString() + { + return "Payload[" + data + "]"; + } + } + + public static void main(String[] argv) throws Exception + { + ObjectMessageTest test = new ObjectMessageTest(); + test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0]; + test.setUp(); + if (argv.length > 1) + { + test._count = Integer.parseInt(argv[1]); + } + + test.test(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ObjectMessageTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.java new file mode 100644 index 0000000000..dce9667ff2 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PropertyValueTest.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.test.unit.basic; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.message.AMQMessage; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class PropertyValueTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(PropertyValueTest.class); + + private int count = 0; + private AMQConnection _connection; + private Destination _destination; + private AMQSession _session; + private final List received = new ArrayList(); + private final List messages = new ArrayList(); + private int _count = 1; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killVMBroker(1); + } + + private void init(AMQConnection connection) throws Exception + { + Destination destination = new AMQQueue(connection, randomize("PropertyValueTest"), true); + init(connection, destination); + } + + private void init(AMQConnection connection, Destination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // set up a slow consumer + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + } + + public void testOnce() + { + runBatch(1); + } + + public void test50() + { + runBatch(50); + } + + private void runBatch(int runSize) + { + try + { + int run = 0; + while (run < runSize) + { + _logger.error("Run Number:" + run++); + try + { + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + catch (Exception e) + { + fail("Unable to initialilse connection: " + e); + } + + int count = _count; + send(count); + waitFor(count); + check(); + _logger.info("Completed without failure"); + + Thread.sleep(10); + _connection.close(); + + _logger.error("End Run Number:" + (run - 1)); + } + } + catch (Exception e) + { + _logger.error(e.getMessage(), e); + e.printStackTrace(); + } + } + + void send(int count) throws JMSException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + String text = "Message " + i; + messages.add(text); + Message m = _session.createTextMessage(text); + + m.setBooleanProperty("Bool", true); + + m.setByteProperty("Byte", (byte) Byte.MAX_VALUE); + m.setDoubleProperty("Double", (double) Double.MAX_VALUE); + m.setFloatProperty("Float", (float) Float.MAX_VALUE); + m.setIntProperty("Int", (int) Integer.MAX_VALUE); + + m.setJMSCorrelationID("Correlation"); + // fixme the m.setJMSMessage has no effect + producer.setPriority(8); + m.setJMSPriority(3); + + // Queue + Queue q; + + if ((i / 2) == 0) + { + q = _session.createTemporaryQueue(); + } + else + { + q = new AMQQueue(_connection, "TestReply"); + } + + m.setJMSReplyTo(q); + m.setStringProperty("TempQueue", q.toString()); + + _logger.trace("Message:" + m); + + Assert.assertEquals("Check temp queue has been set correctly", m.getJMSReplyTo().toString(), + m.getStringProperty("TempQueue")); + + m.setJMSType("Test"); + m.setLongProperty("UnsignedInt", (long) 4294967295L); + m.setLongProperty("Long", (long) Long.MAX_VALUE); + + m.setShortProperty("Short", (short) Short.MAX_VALUE); + m.setStringProperty("String", "Test"); + + // AMQP Specific values + + // Timestamp + long nano = System.nanoTime(); + m.setStringProperty("time-str", String.valueOf(nano)); + ((AMQMessage) m).setTimestampProperty(new AMQShortString("time"), nano); + + // Decimal + BigDecimal bd = new BigDecimal(Integer.MAX_VALUE); + ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal"), bd.setScale(Byte.MAX_VALUE)); + + bd = new BigDecimal((long) Integer.MAX_VALUE + 1L); + + try + { + ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal-bad-value"), bd.setScale(Byte.MAX_VALUE)); + fail("UnsupportedOperationException should be thrown as value can't be correctly transmitted"); + } + catch (UnsupportedOperationException uoe) + { + // normal path. + } + + try + { + ((AMQMessage) m).setDecimalProperty(new AMQShortString("decimal-bad-scale"), + bd.setScale(Byte.MAX_VALUE + 1)); + fail("UnsupportedOperationException should be thrown as scale can't be correctly transmitted"); + } + catch (UnsupportedOperationException uoe) + { + // normal path. + } + + // Void + ((AMQMessage) m).setVoidProperty(new AMQShortString("void")); + + _logger.debug("Sending Msg:" + m); + producer.send(m); + } + } + + void waitFor(int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + received.wait(); + } + } + } + + void check() throws JMSException + { + List actual = new ArrayList(); + for (JMSTextMessage m : received) + { + actual.add(m.getText()); + + // Check Properties + + Assert.assertEquals("Check Boolean properties are correctly transported", true, m.getBooleanProperty("Bool")); + Assert.assertEquals("Check Byte properties are correctly transported", (byte) Byte.MAX_VALUE, + m.getByteProperty("Byte")); + Assert.assertEquals("Check Double properties are correctly transported", (double) Double.MAX_VALUE, + m.getDoubleProperty("Double")); + Assert.assertEquals("Check Float properties are correctly transported", (float) Float.MAX_VALUE, + m.getFloatProperty("Float")); + Assert.assertEquals("Check Int properties are correctly transported", (int) Integer.MAX_VALUE, + m.getIntProperty("Int")); + Assert.assertEquals("Check CorrelationID properties are correctly transported", "Correlation", + m.getJMSCorrelationID()); + Assert.assertEquals("Check Priority properties are correctly transported", 8, m.getJMSPriority()); + + // Queue + Assert.assertEquals("Check ReplyTo properties are correctly transported", m.getStringProperty("TempQueue"), + m.getJMSReplyTo().toString()); + + Assert.assertEquals("Check Type properties are correctly transported", "Test", m.getJMSType()); + + Assert.assertEquals("Check Short properties are correctly transported", (short) Short.MAX_VALUE, + m.getShortProperty("Short")); + Assert.assertEquals("Check UnsignedInt properties are correctly transported", (long) 4294967295L, + m.getLongProperty("UnsignedInt")); + Assert.assertEquals("Check Long properties are correctly transported", (long) Long.MAX_VALUE, + m.getLongProperty("Long")); + Assert.assertEquals("Check String properties are correctly transported", "Test", m.getStringProperty("String")); + + // AMQP Tests Specific values + + Assert.assertEquals("Check Timestamp properties are correctly transported", m.getStringProperty("time-str"), + ((AMQMessage) m).getTimestampProperty(new AMQShortString("time")).toString()); + + // Decimal + BigDecimal bd = new BigDecimal(Integer.MAX_VALUE); + + Assert.assertEquals("Check decimal properties are correctly transported", bd.setScale(Byte.MAX_VALUE), + ((AMQMessage) m).getDecimalProperty(new AMQShortString("decimal"))); + + // Void + ((AMQMessage) m).setVoidProperty(new AMQShortString("void")); + + Assert.assertTrue("Check void properties are correctly transported", + ((AMQMessage) m).getPropertyHeaders().containsKey("void")); + } + + received.clear(); + + assertEqual(messages.iterator(), actual.iterator()); + + messages.clear(); + } + + private static void assertEqual(Iterator expected, Iterator actual) + { + List errors = new ArrayList(); + while (expected.hasNext() && actual.hasNext()) + { + try + { + assertEqual(expected.next(), actual.next()); + } + catch (Exception e) + { + errors.add(e.getMessage()); + } + } + while (expected.hasNext()) + { + errors.add("Expected " + expected.next() + " but no more actual values."); + } + while (actual.hasNext()) + { + errors.add("Found " + actual.next() + " but no more expected values."); + } + + if (!errors.isEmpty()) + { + throw new RuntimeException(errors.toString()); + } + } + + private static void assertEqual(Object expected, Object actual) + { + if (!expected.equals(actual)) + { + throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'"); + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + received.add((JMSTextMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + PropertyValueTest test = new PropertyValueTest(); + test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0]; + test.setUp(); + if (argv.length > 1) + { + test._count = Integer.parseInt(argv[1]); + } + + test.testOnce(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(PropertyValueTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.java new file mode 100644 index 0000000000..a3d0cf6dcd --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/PubSubTwoConnectionTest.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.test.unit.basic; + +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; + +/** + * @author Apache Software Foundation + */ +public class PubSubTwoConnectionTest extends TestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + /** + * This tests that a consumer is set up synchronously + * @throws Exception + */ + public void testTwoConnections() throws Exception + { + + AMQConnection con1 = new AMQConnection("vm://:1", "guest", "guest", "Client1", "test"); + + Topic topic = new AMQTopic(con1, "MyTopic"); + + Session session1 = con1.createSession(false, AMQSession.NO_ACKNOWLEDGE); + MessageProducer producer = session1.createProducer(topic); + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "Client2", "test"); + Session session2 = con2.createSession(false, AMQSession.NO_ACKNOWLEDGE); + MessageConsumer consumer = session2.createConsumer(topic); + con2.start(); + producer.send(session1.createTextMessage("Hello")); + TextMessage tm1 = (TextMessage) consumer.receive(2000); + assertNotNull(tm1); + assertEquals("Hello", tm1.getText()); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.java new file mode 100644 index 0000000000..668233f356 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/ReceiveTest.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.test.unit.basic; + +import javax.jms.MessageConsumer; + +import junit.framework.TestCase; + +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.client.transport.TransportConnection; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; + +public class ReceiveTest extends TestCase +{ + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private MessageConsumer _consumer; + + private static final String VM_BROKER = "vm://:1"; + public String _connectionString = VM_BROKER; + + protected void setUp() throws Exception + { + super.setUp(); + if (_connectionString.equals(VM_BROKER)) + { + createVMBroker(); + String broker = _connectionString; + init(new AMQConnection(broker, "guest", "guest", "ReceiveTestClient", "test")); + } + } + + public void createVMBroker() + { + try + { + TransportConnection.createVMBroker(1); + } + catch (AMQVMBrokerCreationException e) + { + fail("Unable to create broker: " + e); + } + } + + protected void tearDown() throws Exception + { + if (_connectionString.equals(VM_BROKER)) + { + TransportConnection.killVMBroker(1); + } + super.tearDown(); + } + + private void init(AMQConnection connection) throws Exception + { + init(connection, new AMQQueue(connection,"ReceiveTest", true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(true, AMQSession.NO_ACKNOWLEDGE); + _consumer = _session.createConsumer(_destination); + _connection.start(); + } + + public void test() throws Exception + { + _consumer.receive(5000); + _connection.close(); + } + + public static void main(String[] argv) throws Exception + { + ReceiveTest test = new ReceiveTest(); + test._connectionString = argv.length == 0 ? VM_BROKER : argv[0]; + test.setUp(); + test.test(); + test.tearDown(); + } + + public static junit.framework.Test suite() + { + // TODO: note that this test doesn't use the VMBrokerSetup + // test helper class to create and tear down its + // VMBroker. This is because the main() above seems to + // indicate that it's also used outside of the surefire test + // framework. If it isn't, then this test should also be + // changed to use VMBrokerSetup here. + return new junit.framework.TestSuite(ReceiveTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.java new file mode 100644 index 0000000000..40c712c1c9 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SelectorTest.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.basic; + +import junit.framework.TestCase; + +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.client.BasicMessageProducer; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; + +public class SelectorTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(SelectorTest.class); + + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private int count; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + private void init(AMQConnection connection) throws Exception + { + init(connection, new AMQQueue(connection, randomize("SessionStartTest"), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + connection.start(); + + String selector = null; + // selector = "Cost = 2 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; + // selector = "JMSType = Special AND Cost = 2 AND AMQMessageID > 0 AND JMSDeliveryMode=" + DeliveryMode.NON_PERSISTENT; + + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + // _session.createConsumer(destination).setMessageListener(this); + _session.createConsumer(destination, selector).setMessageListener(this); + } + + public synchronized void test() throws JMSException, InterruptedException + { + try + { + Message msg = _session.createTextMessage("Message"); + msg.setJMSPriority(1); + msg.setIntProperty("Cost", 2); + msg.setJMSType("Special"); + + _logger.info("Sending Message:" + msg); + + ((BasicMessageProducer) _session.createProducer(_destination)).send(msg, DeliveryMode.NON_PERSISTENT); + _logger.info("Message sent, waiting for response..."); + wait(1000); + + if (count > 0) + { + _logger.info("Got message"); + } + + if (count == 0) + { + fail("Did not get message!"); + // throw new RuntimeException("Did not get message!"); + } + } + finally + { + _session.close(); + _connection.close(); + } + } + + public synchronized void onMessage(Message message) + { + count++; + _logger.info("Got Message:" + message); + notify(); + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + SelectorTest test = new SelectorTest(); + test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0]; + test.setUp(); + test.test(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(SelectorTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java new file mode 100644 index 0000000000..cc18169a5b --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.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.test.unit.basic; + +import junit.framework.TestCase; + +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.framing.AMQShortString; +import org.apache.qpid.testutil.VMBrokerSetup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; + +public class SessionStartTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(SessionStartTest.class); + + private AMQConnection _connection; + private AMQDestination _destination; + private AMQSession _session; + private int count; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + private void init(AMQConnection connection) throws Exception + { + init(connection, + new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("SessionStartTest")), true)); + } + + private void init(AMQConnection connection, AMQDestination destination) throws Exception + { + _connection = connection; + _destination = destination; + connection.start(); + + _session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _session.createConsumer(destination).setMessageListener(this); + } + + public synchronized void test() throws JMSException, InterruptedException + { + try + { + _session.createProducer(_destination).send(_session.createTextMessage("Message")); + _logger.info("Message sent, waiting for response..."); + wait(1000); + if (count > 0) + { + _logger.info("Got message"); + } + else + { + throw new RuntimeException("Did not get message!"); + } + } + finally + { + _session.close(); + _connection.close(); + } + } + + public synchronized void onMessage(Message message) + { + count++; + notify(); + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + SessionStartTest test = new SessionStartTest(); + test._connectionString = (argv.length == 0) ? "localhost:5672" : argv[0]; + test.setUp(); + test.test(); + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(SessionStartTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java new file mode 100644 index 0000000000..000fb9ab88 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/TextMessageTest.java @@ -0,0 +1,257 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.testutil.VMBrokerSetup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageNotWriteableException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class TextMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(TextMessageTest.class); + + private AMQConnection _connection; + private Destination _destination; + private AMQSession _session; + private final List received = new ArrayList(); + private final List messages = new ArrayList(); + private int _count = 100; + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + try + { + init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test")); + } + catch (Exception e) + { + fail("Unable to initialilse connection: " + e); + } + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + private void init(AMQConnection connection) throws Exception + { + Destination destination = + new AMQQueue(connection.getDefaultQueueExchangeName(), new AMQShortString(randomize("TextMessageTest")), true); + init(connection, destination); + } + + private void init(AMQConnection connection, Destination destination) throws Exception + { + _connection = connection; + _destination = destination; + _session = (AMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // set up a slow consumer + _session.createConsumer(destination).setMessageListener(this); + connection.start(); + } + + public void test() throws Exception + { + int count = _count; + send(count); + waitFor(count); + check(); + _logger.info("Completed without failure"); + _connection.close(); + } + + void send(int count) throws JMSException + { + // create a publisher + MessageProducer producer = _session.createProducer(_destination); + for (int i = 0; i < count; i++) + { + String text = "Message " + i; + messages.add(text); + Message m = _session.createTextMessage(text); + m.setStringProperty("String", "hello"); + + _logger.info("Sending Msg:" + m); + producer.send(m); + } + } + + void waitFor(int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + received.wait(); + } + } + } + + void check() throws JMSException + { + List actual = new ArrayList(); + for (JMSTextMessage m : received) + { + actual.add(m.getText()); + + // Check body write status + try + { + m.setText("Test text"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearBody(); + + try + { + m.setText("Test text"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + // Check property write status + try + { + m.setStringProperty("test", "test"); + Assert.fail("Message should not be writeable"); + } + catch (MessageNotWriteableException mnwe) + { + // normal execution + } + + m.clearProperties(); + + try + { + m.setStringProperty("test", "test"); + } + catch (MessageNotWriteableException mnwe) + { + Assert.fail("Message should be writeable"); + } + + } + + assertEqual(messages.iterator(), actual.iterator()); + } + + private static void assertEqual(Iterator expected, Iterator actual) + { + List errors = new ArrayList(); + while (expected.hasNext() && actual.hasNext()) + { + try + { + assertEqual(expected.next(), actual.next()); + } + catch (Exception e) + { + errors.add(e.getMessage()); + } + } + while (expected.hasNext()) + { + errors.add("Expected " + expected.next() + " but no more actual values."); + } + while (actual.hasNext()) + { + errors.add("Found " + actual.next() + " but no more expected values."); + } + + if (!errors.isEmpty()) + { + throw new RuntimeException(errors.toString()); + } + } + + private static void assertEqual(Object expected, Object actual) + { + if (!expected.equals(actual)) + { + throw new RuntimeException("Expected '" + expected + "' found '" + actual + "'"); + } + } + + public void onMessage(Message message) + { + synchronized (received) + { + received.add((JMSTextMessage) message); + received.notify(); + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] argv) throws Exception + { + TextMessageTest test = new TextMessageTest(); + test._connectionString = (argv.length == 0) ? "vm://:1" : argv[0]; + test.setUp(); + if (argv.length > 1) + { + test._count = Integer.parseInt(argv[1]); + } + + test.test(); + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(TextMessageTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java new file mode 100644 index 0000000000..690ba7f01b --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/basic/close/CloseTests.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.test.unit.basic.close; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.URLSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +public class CloseTests extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(CloseTests.class); + + private static final String BROKER = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.setUp(); + + TransportConnection.killVMBroker(1); + } + + public void testCloseQueueReceiver() throws AMQException, URLSyntaxException, JMSException + { + AMQConnection connection = new AMQConnection(BROKER, "guest", "guest", this.getName(), "test"); + + Session session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + AMQQueue queue = new AMQQueue(new AMQBindingURL("test-queue")); + MessageConsumer consumer = session.createConsumer(queue); + + MessageProducer producer_not_used_but_created_for_testing = session.createProducer(queue); + + connection.start(); + + _logger.info("About to close consumer"); + + consumer.close(); + + _logger.info("Closed Consumer"); + + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.java new file mode 100644 index 0000000000..0e15341615 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQConnectionTest.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.test.unit.client; + +import javax.jms.JMSException; +import javax.jms.QueueSession; +import javax.jms.TopicSession; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; + +public class AMQConnectionTest extends TestCase +{ + private static AMQConnection _connection; + private static AMQTopic _topic; + private static AMQQueue _queue; + private static QueueSession _queueSession; + private static TopicSession _topicSession; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + _connection = new AMQConnection("vm://:1", "guest", "guest", "fred", "test"); + _topic = new AMQTopic(_connection.getDefaultTopicExchangeName(), new AMQShortString("mytopic")); + _queue = new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("myqueue")); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + try + { + _connection.close(); + } + catch (JMSException e) + { + //ignore + } + TransportConnection.killAllVMBrokers(); + } + + /** + * Simple tests to check we can create TopicSession and QueueSession ok + * And that they throw exceptions where appropriate as per JMS spec + */ + + public void testCreateQueueSession() throws JMSException + { + _queueSession = _connection.createQueueSession(false, AMQSession.NO_ACKNOWLEDGE); + } + + public void testCreateTopicSession() throws JMSException + { + _topicSession = _connection.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + } + + public void testTopicSessionCreateBrowser() throws JMSException + { + try + { + _topicSession.createBrowser(_queue); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testTopicSessionCreateQueue() throws JMSException + { + try + { + _topicSession.createQueue("abc"); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testTopicSessionCreateTemporaryQueue() throws JMSException + { + try + { + _topicSession.createTemporaryQueue(); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testQueueSessionCreateTemporaryTopic() throws JMSException + { + try + { + _queueSession.createTemporaryTopic(); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testQueueSessionCreateTopic() throws JMSException + { + try + { + _queueSession.createTopic("abc"); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testQueueSessionDurableSubscriber() throws JMSException + { + try + { + _queueSession.createDurableSubscriber(_topic, "abc"); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public void testQueueSessionUnsubscribe() throws JMSException + { + try + { + _queueSession.unsubscribe("abc"); + fail("expected exception did not occur"); + } + catch (javax.jms.IllegalStateException s) + { + // ok + } + catch (Exception e) + { + fail("expected javax.jms.IllegalStateException, got " + e); + } + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(AMQConnectionTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.java new file mode 100644 index 0000000000..78b7976f55 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/AMQSessionTest.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.test.unit.client; + +import javax.jms.JMSException; +import javax.jms.QueueReceiver; +import javax.jms.TopicSubscriber; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.testutil.VMBrokerSetup; + +/** + * Tests for QueueReceiver and TopicSubscriber creation methods on AMQSession + */ +public class AMQSessionTest extends TestCase +{ + + private static AMQSession _session; + private static AMQTopic _topic; + private static AMQQueue _queue; + private static AMQConnection _connection; + + protected void setUp() throws Exception + { + super.setUp(); + _connection = new AMQConnection("vm://:1", "guest", "guest", "fred", "test"); + _topic = new AMQTopic(_connection,"mytopic"); + _queue = new AMQQueue(_connection,"myqueue"); + _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + } + + protected void tearDown() throws Exception + { + try + { + _connection.close(); + } + catch (JMSException e) + { + //just close + } + super.tearDown(); + } + + public void testCreateSubscriber() throws JMSException + { + TopicSubscriber subscriber = _session.createSubscriber(_topic); + assertEquals("Topic names should match from TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName()); + + subscriber = _session.createSubscriber(_topic, "abc", false); + assertEquals("Topic names should match from TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName()); + } + + public void testCreateDurableSubscriber() throws JMSException + { + TopicSubscriber subscriber = _session.createDurableSubscriber(_topic, "mysubname"); + assertEquals("Topic names should match from durable TopicSubscriber", _topic.getTopicName(), subscriber.getTopic().getTopicName()); + + subscriber = _session.createDurableSubscriber(_topic, "mysubname", "abc", false); + assertEquals("Topic names should match from durable TopicSubscriber with selector", _topic.getTopicName(), subscriber.getTopic().getTopicName()); + } + + public void testCreateQueueReceiver() throws JMSException + { + QueueReceiver receiver = _session.createQueueReceiver(_queue); + assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName()); + + receiver = _session.createQueueReceiver(_queue, "abc"); + assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName()); + } + + public void testCreateReceiver() throws JMSException + { + QueueReceiver receiver = _session.createReceiver(_queue); + assertEquals("Queue names should match from QueueReceiver", _queue.getQueueName(), receiver.getQueue().getQueueName()); + + receiver = _session.createReceiver(_queue, "abc"); + assertEquals("Queue names should match from QueueReceiver with selector", _queue.getQueueName(), receiver.getQueue().getQueueName()); + } + + public static void stopVmBrokers() + { + _queue = null; + _topic = null; + _session = null; + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(AMQSessionTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.java new file mode 100644 index 0000000000..e513f25e3d --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/BrokerDetails/BrokerDetailsTest.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.test.unit.client.BrokerDetails; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQBrokerDetails; +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.getOption("timeout").equals("200")); + assertTrue(broker.getOption("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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.java new file mode 100644 index 0000000000..575d542633 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseMethodHandlerNoCloseOk.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.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.AMQStateManager; +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.apache.qpid.protocol.AMQMethodEvent; + +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(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + _logger.debug("ChannelClose method received"); + ChannelCloseBody method = (ChannelCloseBody) evt.getMethod(); + + AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); + AMQShortString reason = method.replyText; + 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); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + throw new AMQNoRouteException("Error: " + reason, null); + } + else if (errorCode == AMQConstant.INVALID_ARGUMENT) + { + _logger.debug("Broker responded with Invalid Argument."); + + throw new AMQInvalidArgumentException(String.valueOf(reason)); + } + else if (errorCode == AMQConstant.INVALID_ROUTING_KEY) + { + _logger.debug("Broker responded with Invalid Routing Key."); + + throw new AMQInvalidRoutingKeyException(String.valueOf(reason)); + } + else + { + throw new AMQChannelClosedException(errorCode, "Error: " + reason); + } + + } + + protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java new file mode 100644 index 0000000000..559e9a4741 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.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.test.unit.client.channelclose; + +import junit.framework.TestCase; + +import junit.textui.TestRunner; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Due to bizarre exception handling all sessions are closed if you get + * a channel close request and no exception listener is registered. + *

    + * JIRA issue IBTBLZ-10. + *

    + * Simulate by: + *

    + * 0. Create two sessions with no exception listener. + * 1. Publish message to queue/topic that does not exist (wrong routing key). + * 2. This will cause a channel close. + * 3. Since client does not have an exception listener, currently all sessions are + * closed. + */ +public class ChannelCloseOkTest extends TestCase +{ + private AMQConnection _connection; + private Destination _destination1; + private Destination _destination2; + private Session _session1; + private Session _session2; + private final List _received1 = new ArrayList(); + private final List _received2 = new ArrayList(); + + private static final Logger _log = LoggerFactory.getLogger(ChannelCloseOkTest.class); + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + + TransportConnection.createVMBroker(1); + _connection = new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"); + + _destination1 = new AMQQueue(_connection, "q1", true); + _destination2 = new AMQQueue(_connection, "q2", true); + _session1 = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _session1.createConsumer(_destination1).setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _log.debug("consumer 1 got message [" + getTextMessage(message) + "]"); + synchronized (_received1) + { + _received1.add(message); + _received1.notify(); + } + } + }); + _session2 = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + _session2.createConsumer(_destination2).setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + _log.debug("consumer 2 got message [" + getTextMessage(message) + "]"); + synchronized (_received2) + { + _received2.add(message); + _received2.notify(); + } + } + }); + + _connection.start(); + } + + private String getTextMessage(Message message) + { + TextMessage tm = (TextMessage) message; + try + { + return tm.getText(); + } + catch (JMSException e) + { + return "oops " + e; + } + } + + protected void tearDown() throws Exception + { + closeConnection(); + TransportConnection.killAllVMBrokers(); + super.tearDown(); + } + + public void closeConnection() throws JMSException + { + if (_connection != null) + { + _log.info(">>>>>>>>>>>>>>.. closing"); + _connection.close(); + } + } + + public void testWithoutExceptionListener() throws Exception + { + doTest(); + } + + public void testWithExceptionListener() throws Exception + { + _connection.setExceptionListener(new ExceptionListener() + { + public void onException(JMSException jmsException) + { + _log.warn("onException - " + jmsException.getMessage()); + } + }); + + doTest(); + } + + public void doTest() throws Exception + { + // Check both sessions are ok. + sendAndWait(_session1, _destination1, "first", _received1, 1); + sendAndWait(_session2, _destination2, "second", _received2, 1); + assertEquals(1, _received1.size()); + assertEquals(1, _received2.size()); + + // Now send message to incorrect destination on session 1. + Destination destination = new AMQQueue(_connection, "incorrect"); + send(_session1, destination, "third"); // no point waiting as message will never be received. + + // Ensure both sessions are still ok. + // Send a bunch of messages as this give time for the sessions to be erroneously closed. + final int num = 300; + for (int i = 0; i < num; ++i) + { + send(_session1, _destination1, "" + i); + send(_session2, _destination2, "" + i); + } + + waitFor(_received1, num + 1); + waitFor(_received2, num + 1); + + // Note that the third message is never received as it is sent to an incorrect destination. + assertEquals(num + 1, _received1.size()); + assertEquals(num + 1, _received2.size()); + } + + private void sendAndWait(Session session, Destination destination, String message, List received, int count) + throws JMSException, InterruptedException + { + send(session, destination, message); + waitFor(received, count); + } + + private void send(Session session, Destination destination, String message) throws JMSException + { + _log.debug("sending message " + message); + MessageProducer producer1 = session.createProducer(destination); + producer1.send(session.createTextMessage(message)); + } + + private void waitFor(List received, int count) throws InterruptedException + { + synchronized (received) + { + while (received.size() < count) + { + try + { + received.wait(); + } + catch (InterruptedException e) + { + _log.info("Interrupted: " + e); + throw e; + } + } + } + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static void main(String[] args) + { + TestRunner.run(ChannelCloseOkTest.class); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ChannelCloseOkTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java new file mode 100644 index 0000000000..f1099ca5ab --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQTimeoutException; +import org.apache.qpid.client.AMQConnection; +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.protocol.AMQProtocolSession; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ChannelCloseOkBody; +import org.apache.qpid.framing.ChannelOpenBody; +import org.apache.qpid.framing.ChannelOpenOkBody; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeclareOkBody; +import org.apache.qpid.jms.ConnectionListener; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.url.URLSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +public class ChannelCloseTest extends TestCase implements ExceptionListener, ConnectionListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseTest.class); + + Connection _connection; + private String _brokerlist = "vm://:1"; + private Session _session; + private static final long SYNC_TIMEOUT = 5000; + private int TEST = 0; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + /* + close channel, use chanel with same id ensure error. + */ + public void testReusingChannelAfterFullClosure() throws Exception + { + _connection = newConnection(); + + // Create Producer + try + { + _connection.start(); + + createChannelAndTest(1); + + // Cause it to close + try + { + _logger.info("Testing invalid exchange"); + declareExchange(1, "", "name_that_will_lookup_to_null", false); + fail("Exchange name is empty so this should fail "); + } + catch (AMQException e) + { + assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode()); + } + + // Check that + try + { + _logger.info("Testing valid exchange should fail"); + declareExchange(1, "topic", "amq.topic", false); + fail("This should not succeed as the channel should be closed "); + } + catch (AMQException e) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Exception occured was:" + e.getErrorCode()); + } + + assertEquals("Connection should be closed", AMQConstant.CHANNEL_ERROR, e.getErrorCode()); + + _connection = newConnection(); + } + + checkSendingMessage(); + + _session.close(); + _connection.close(); + + } + catch (JMSException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + /* + close channel and send guff then send ok no errors + */ + public void testSendingMethodsAfterClose() throws Exception + { + try + { + _connection = new AMQConnection("amqp://guest:guest@CCTTest/test?brokerlist='" + _brokerlist + "'"); + + ((AMQConnection) _connection).setConnectionListener(this); + + _connection.setExceptionListener(this); + + // Change the StateManager for one that doesn't respond with Close-OKs + AMQStateManager oldStateManager = ((AMQConnection) _connection).getProtocolHandler().getStateManager(); + + _session = _connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + _connection.start(); + + // Test connection + checkSendingMessage(); + + // Set StateManager to manager that ignores Close-oks + AMQProtocolSession protocolSession = ((AMQConnection) _connection).getProtocolHandler().getProtocolSession(); + AMQStateManager newStateManager = new NoCloseOKStateManager(protocolSession); + newStateManager.changeState(oldStateManager.getCurrentState()); + + ((AMQConnection) _connection).getProtocolHandler().setStateManager(newStateManager); + + final int TEST_CHANNEL = 1; + _logger.info("Testing Channel(" + TEST_CHANNEL + ") Creation"); + + createChannelAndTest(TEST_CHANNEL); + + // Cause it to close + try + { + _logger.info("Closing Channel - invalid exchange"); + declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", false); + fail("Exchange name is empty so this should fail "); + } + catch (AMQException e) + { + assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode()); + } + + try + { + // Send other methods that should be ignored + // send them no wait as server will ignore them + _logger.info("Tested known exchange - should ignore"); + declareExchange(TEST_CHANNEL, "topic", "amq.topic", true); + + _logger.info("Tested known invalid exchange - should ignore"); + declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", true); + + _logger.info("Tested known invalid exchange - should ignore"); + declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", true); + + // Send sync .. server will igore and timy oue + _logger.info("Tested known invalid exchange - should ignore"); + declareExchange(TEST_CHANNEL, "", "name_that_will_lookup_to_null", false); + } + catch (AMQTimeoutException te) + { + assertEquals("Request should timeout", AMQConstant.REQUEST_TIMEOUT, te.getErrorCode()); + } + catch (AMQException e) + { + fail("This should not fail as all requests should be ignored"); + } + + _logger.info("Sending Close"); + // Send Close-ok + sendClose(TEST_CHANNEL); + + _logger.info("Re-opening channel"); + + createChannelAndTest(TEST_CHANNEL); + + // Test connection is still ok + + checkSendingMessage(); + + } + catch (JMSException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + catch (AMQException e) + { + fail(e.getMessage()); + + } + catch (URLSyntaxException e) + { + fail(e.getMessage()); + } + finally + { + try + { + _session.close(); + _connection.close(); + } + catch (JMSException e) + { + e.printStackTrace(); + fail(e.getMessage()); + } + } + } + + private void createChannelAndTest(int channel) throws FailoverException + { + // Create A channel + try + { + createChannel(channel); + } + catch (AMQException e) + { + fail(e.getMessage()); + } + + // Test it is ok + try + { + declareExchange(channel, "topic", "amq.topic", false); + _logger.info("Tested known exchange"); + } + catch (AMQException e) + { + fail("This should not fail as this is the default exchange details"); + } + } + + private void sendClose(int channel) + { + AMQFrame frame = + ChannelCloseOkBody.createAMQFrame(channel, + ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(), + ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion()); + + ((AMQConnection) _connection).getProtocolHandler().writeFrame(frame); + } + + private void checkSendingMessage() throws JMSException + { + TEST++; + _logger.info("Test creating producer which will use channel id 1"); + + Queue queue = _session.createTemporaryQueue(); + + MessageConsumer consumer = _session.createConsumer(queue); + + MessageProducer producer = _session.createProducer(queue); + + final String MESSAGE = "CCT_Test_Message"; + producer.send(_session.createTextMessage(MESSAGE)); + + Message msg = consumer.receive(2000); + + assertNotNull("Received messages should not be null.", msg); + assertEquals("Message received not what we sent", MESSAGE, ((TextMessage) msg).getText()); + } + + private Connection newConnection() + { + AMQConnection connection = null; + try + { + connection = new AMQConnection("amqp://guest:guest@CCTTest/test?brokerlist='" + _brokerlist + "'"); + + connection.setConnectionListener(this); + + _session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + connection.start(); + + } + catch (JMSException e) + { + fail("Creating new connection when:" + e.getMessage()); + } + catch (AMQException e) + { + fail("Creating new connection when:" + e.getMessage()); + } + catch (URLSyntaxException e) + { + fail("Creating new connection when:" + e.getMessage()); + } + + return connection; + } + + private void declareExchange(final int channelId, final String _type, final String _name, final boolean nowait) + throws AMQException, FailoverException + { +// new FailoverRetrySupport(new FailoverProtectedOperation() +// { +// public Object execute() throws AMQException, FailoverException +// { + + AMQProtocolHandler protocolHandler = ((AMQConnection) _connection).getProtocolHandler(); + + AMQFrame exchangeDeclare = + ExchangeDeclareBody.createAMQFrame(channelId, + protocolHandler.getProtocolMajorVersion(), + protocolHandler.getProtocolMinorVersion(), null, // arguments + false, // autoDelete + false, // durable + new AMQShortString(_name), // exchange + false, // internal + nowait, // nowait + true, // passive + 0, // ticket + new AMQShortString(_type)); // type + + if (nowait) + { + protocolHandler.writeFrame(exchangeDeclare); + } + else + { + protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class, SYNC_TIMEOUT); + } + +// return null; +// } +// }, (AMQConnection)_connection).execute(); + + } + + private void createChannel(int channelId) throws AMQException, FailoverException + { + ((AMQConnection) _connection).getProtocolHandler().syncWrite(ChannelOpenBody.createAMQFrame(channelId, + ((AMQConnection) _connection).getProtocolHandler().getProtocolMajorVersion(), + ((AMQConnection) _connection).getProtocolHandler().getProtocolMinorVersion(), null), // outOfBand + ChannelOpenOkBody.class); + + } + + public void onException(JMSException jmsException) + { + // _logger.info("CCT" + jmsException); + fail(jmsException.getMessage()); + } + + public void bytesSent(long count) + { + } + + public void bytesReceived(long count) + { + } + + public boolean preFailover(boolean redirect) + { + return false; + } + + public boolean preResubscribe() + { + return false; + } + + public void failoverComplete() + { + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java new file mode 100644 index 0000000000..9600d1e9d3 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/CloseWithBlockingReceiveTest.java @@ -0,0 +1,87 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.Session; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; + +/** + * @author Apache Software Foundation + */ +public class CloseWithBlockingReceiveTest extends TestCase +{ + + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + + public void testReceiveReturnsNull() throws Exception + { + final AMQConnection connection = new AMQConnection("vm://:1", "guest", "guest", + "fred", "test"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(new AMQTopic(connection, "banana")); + connection.start(); + + Runnable r = new Runnable() + { + + public void run() + { + try + { + Thread.sleep(1000); + connection.close(); + } + catch (Exception e) + { + } + } + }; + long startTime = System.currentTimeMillis(); + new Thread(r).start(); + consumer.receive(10000); + assertTrue(System.currentTimeMillis() - startTime < 10000); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(CloseWithBlockingReceiveTest.class); + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java new file mode 100644 index 0000000000..d128f30727 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/NoCloseOKStateManager.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + * + */ +package org.apache.qpid.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); + } + + protected void registerListeners() + { + Map frame2handlerMap = new HashMap(); + + // we need to register a map for the null (i.e. all state) handlers otherwise you get + // a stack overflow in the handler searching code when you present it with a frame for which + // no handlers are registered + // + _state2HandlersMap.put(null, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionTuneBody.class, ConnectionTuneMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap); + + frame2handlerMap = new HashMap(); + frame2handlerMap.put(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap); + + // + // ConnectionOpen handlers + // + frame2handlerMap = new HashMap(); + // Use Test Handler for Close methods to not send Close-OKs + frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseMethodHandlerNoCloseOk.getInstance()); + + frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkMethodHandler.getInstance()); + frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance()); + frame2handlerMap.put(BasicDeliverBody.class, BasicDeliverMethodHandler.getInstance()); + frame2handlerMap.put(BasicReturnBody.class, BasicReturnMethodHandler.getInstance()); + frame2handlerMap.put(BasicCancelOkBody.class, BasicCancelOkMethodHandler.getInstance()); + frame2handlerMap.put(ChannelFlowOkBody.class, ChannelFlowOkMethodHandler.getInstance()); + frame2handlerMap.put(QueueDeleteOkBody.class, QueueDeleteOkMethodHandler.getInstance()); + frame2handlerMap.put(ExchangeBoundOkBody.class, ExchangeBoundOkMethodHandler.getInstance()); + _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap); + } + + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.java new file mode 100644 index 0000000000..1d108b9c5c --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionStartTest.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.test.unit.client.connection; + +import junit.framework.TestCase; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.transport.TransportConnection; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Queue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * ConnectionStartTest: + * This test verifies that a fresh connection is not started and no messages are delivered until the connection is + * started. + * + * After the connection is started then the message should be there, and the connection started. + * + * This Test verifies that using receive() and a messageListener does not cause message delivery before start is called. + * + */ +public class ConnectionStartTest extends TestCase +{ + + String _broker = "vm://:1"; + + AMQConnection _connection; + private Session _consumerSess; + private MessageConsumer _consumer; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + try + { + // Create Consumer Connection + _connection = new AMQConnection(_broker, "guest", "guest", "fred", "test"); + + _consumerSess = _connection.createSession(false, AMQSession.AUTO_ACKNOWLEDGE); + + Queue queue = _consumerSess.createQueue("ConnectionStartTest"); + + _consumer = _consumerSess.createConsumer(queue); + + + // Create Producer Connection to send message + AMQConnection pubCon = new AMQConnection(_broker, "guest", "guest", "fred", "test"); + + Session pubSess = pubCon.createSession(false, AMQSession.AUTO_ACKNOWLEDGE); + + MessageProducer pub = pubSess.createProducer(queue); + + pub.send(pubSess.createTextMessage("Initial Message")); + + pubCon.close(); + + } + catch (Exception e) + { + fail("Connection to " + _broker + " should succeed. Reason: " + e); + } + } + + protected void tearDown() throws Exception + { + _connection.close(); + TransportConnection.killVMBroker(1); + super.tearDown(); + } + + public void testSimpleReceiveConnection() + { + try + { + assertTrue("Connection should not be started", !_connection.started()); + //Note that this next line will start the dispatcher in the session + // should really not be called before _connection start + assertNull("There should not be messages waiting for the consumer", _consumer.receiveNoWait()); + _connection.start(); + assertNotNull("There should be messages waiting for the consumer", _consumer.receive(1000)); + assertTrue("Connection should be started", _connection.started()); + + } + catch (JMSException e) + { + fail("An error occured during test because:" + e); + } + + } + + public void testMessageListenerConnection() + { + final CountDownLatch _gotMessage = new CountDownLatch(1); + + try + { + assertTrue("Connection should not be started", !_connection.started()); + _consumerSess.setMessageListener(new MessageListener() + { + public void onMessage(Message message) + { + try + { + assertTrue("Connection should be started", _connection.started()); + assertEquals("Mesage Received", "Initial Message", ((TextMessage) message).getText()); + _gotMessage.countDown(); + } + catch (JMSException e) + { + fail("Couldn't get message text because:" + e.getCause()); + } + } + }); + + // Ensure that setting a ML doesn't start the connection + assertTrue("Connection should not be started", !_connection.started()); + // Ensure that the message wasn't delivered while the connection was stopped. + assertEquals("Message latch should still be set",1,_gotMessage.getCount()); + + _connection.start(); + assertTrue("Connection should be started", _connection.started()); + + try + { + _gotMessage.await(1000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + fail("Timed out awaiting message via onMessage"); + } + + } + catch (JMSException e) + { + fail("Failed because:" + e.getCause()); + } + + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ConnectionStartTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java new file mode 100644 index 0000000000..56394fee27 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connection/ConnectionTest.java @@ -0,0 +1,194 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.AMQConnectionFailureException; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQUnresolvedAddressException; +import org.apache.qpid.client.AMQAuthenticationException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.jms.Session; + +import junit.framework.TestCase; + +import javax.jms.Connection; +import javax.jms.QueueSession; +import javax.jms.TopicSession; + +public class ConnectionTest extends TestCase +{ + + String _broker = "vm://:1"; + String _broker_NotRunning = "vm://:2"; + String _broker_BadDNS = "tcp://hg3sgaaw4lgihjs"; + + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + TransportConnection.killVMBroker(1); + } + + public void testSimpleConnection() + { + try + { + AMQConnection conn = new AMQConnection(_broker, "guest", "guest", "fred", "test"); + conn.close(); + } + catch (Exception e) + { + fail("Connection to " + _broker + " should succeed. Reason: " + e); + } + } + + + public void testDefaultExchanges() + { + try + { + AMQConnection conn = new AMQConnection("amqp://guest:guest@clientid/test?brokerlist='" + + _broker + + "?retries='1''&defaultQueueExchange='test.direct'" + + "&defaultTopicExchange='test.topic'" + + "&temporaryQueueExchange='tmp.direct'" + + "&temporaryTopicExchange='tmp.topic'"); + + QueueSession queueSession = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + AMQQueue queue = (AMQQueue) queueSession.createQueue("MyQueue"); + + assertEquals(queue.getExchangeName().toString(), "test.direct"); + + AMQQueue tempQueue = (AMQQueue) queueSession.createTemporaryQueue(); + + assertEquals(tempQueue.getExchangeName().toString(), "tmp.direct"); + + + queueSession.close(); + + + TopicSession topicSession = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + + AMQTopic topic = (AMQTopic) topicSession.createTopic("silly.topic"); + + assertEquals(topic.getExchangeName().toString(), "test.topic"); + + AMQTopic tempTopic = (AMQTopic) topicSession.createTemporaryTopic(); + + assertEquals(tempTopic.getExchangeName().toString(), "tmp.topic"); + + topicSession.close(); + + + conn.close(); + } + catch (Exception e) + { + fail("Connection to " + _broker + " should succeed. Reason: " + e); + } + } + + //fixme AMQAuthenticationException is not propogaged + public void PasswordFailureConnection() throws Exception + { + try + { + new AMQConnection("amqp://guest:rubbishpassword@clientid/test?brokerlist='" + _broker + "?retries='1''"); + fail("Connection should not be established password is wrong."); + } + catch (AMQException amqe) + { + if (!(amqe instanceof AMQAuthenticationException)) + { + fail("Correct exception not thrown. Excpected 'AMQAuthenticationException' got: " + amqe); + } + } + } + + public void testConnectionFailure() throws Exception + { + try + { + new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_NotRunning + "?retries='0''"); + fail("Connection should not be established"); + } + catch (AMQException amqe) + { + if (!(amqe instanceof AMQConnectionFailureException)) + { + fail("Correct exception not thrown. Excpected 'AMQConnectionException' got: " + amqe); + } + } + + } + + public void testUnresolvedHostFailure() throws Exception + { + try + { + new AMQConnection("amqp://guest:guest@clientid/testpath?brokerlist='" + _broker_BadDNS + "?retries='0''"); + fail("Connection should not be established"); + } + catch (AMQException amqe) + { + if (!(amqe instanceof AMQUnresolvedAddressException)) + { + fail("Correct exception not thrown. Excpected 'AMQUnresolvedAddressException' got: " + amqe); + } + } + } + + public void testClientIdCannotBeChanged() throws Exception + { + Connection connection = new AMQConnection(_broker, "guest", "guest", + "fred", "test"); + try + { + connection.setClientID("someClientId"); + fail("No IllegalStateException thrown when resetting clientid"); + } + catch (javax.jms.IllegalStateException e) + { + // PASS + } + } + + public void testClientIdIsPopulatedAutomatically() throws Exception + { + Connection connection = new AMQConnection(_broker, "guest", "guest", + null, "test"); + assertNotNull(connection.getClientID()); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ConnectionTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java new file mode 100644 index 0000000000..bfbba61913 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java @@ -0,0 +1,481 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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'"; + + 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("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',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 static junit.framework.Test suite() + { + return new junit.framework.TestSuite(ConnectionURLTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.java new file mode 100644 index 0000000000..66be1ebc73 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/destinationurl/DestinationURLTest.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.test.unit.client.destinationurl; + +import junit.framework.TestCase; + +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.URLSyntaxException; + +public class DestinationURLTest extends TestCase +{ + public void testFullURL() throws URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 URLSyntaxException + { + + 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 static junit.framework.Test suite() + { + return new junit.framework.TestSuite(DestinationURLTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java new file mode 100644 index 0000000000..19ef612bcc --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.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.test.unit.client.forwardall; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.framing.AMQShortString; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Message; +import javax.jms.MessageListener; + +/** + * Declare a private temporary response queue, + * send a message to amq.direct with a well known routing key with the + * private response queue as the reply-to destination + * consume responses. + */ +public class Client implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(Client.class); + + private final AMQConnection _connection; + private final AMQSession _session; + private final int _expected; + private int _count; + + Client(String broker, int expected) throws Exception + { + this(connect(broker), expected); + } + + Client(AMQConnection connection, int expected) throws Exception + { + _connection = connection; + _expected = expected; + _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + AMQQueue response = + new AMQQueue(_connection.getDefaultQueueExchangeName(), new AMQShortString("ResponseQueue"), true); + _session.createConsumer(response).setMessageListener(this); + _connection.start(); + AMQQueue service = new SpecialQueue(_connection, "ServiceQueue"); + Message request = _session.createTextMessage("Request!"); + request.setJMSReplyTo(response); + _session.createProducer(service).send(request); + } + + void shutdownWhenComplete() throws Exception + { + waitUntilComplete(); + _connection.close(); + } + + public synchronized void onMessage(Message response) + { + + _logger.info("Received " + (++_count) + " of " + _expected + " responses."); + if (_count == _expected) + { + + notifyAll(); + } + + } + + synchronized void waitUntilComplete() throws Exception + { + + if (_count < _expected) + { + wait(10000L); + } + + if (_count < _expected) + { + throw new Exception("Didn't receive all messages... got " + _count + " expected " + _expected); + } + } + + static AMQConnection connect(String broker) throws Exception + { + return new AMQConnection(broker, "guest", "guest", "Client" + System.currentTimeMillis(), "test"); + } + + public static void main(String[] argv) throws Exception + { + final String connectionString; + final int expected; + if (argv.length == 0) + { + connectionString = "localhost:5672"; + expected = 100; + } + else + { + connectionString = argv[0]; + expected = Integer.parseInt(argv[1]); + } + + new Client(connect(connectionString), expected).shutdownWhenComplete(); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java new file mode 100644 index 0000000000..9cde24dd92 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.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.test.unit.client.forwardall; + +import junit.framework.TestCase; +import org.apache.qpid.testutil.VMBrokerSetup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Runs the Service's and Client parts of the test in the same process + * as the broker + */ +public class CombinedTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(CombinedTest.class); + private int run = 0; + + protected void setUp() throws Exception + { + super.setUp(); + } + + protected void tearDown() throws Exception + { + ServiceCreator.closeAll(); + super.tearDown(); + } + + public void testForwardAll() throws Exception + { + while (run < 10) + { + int services = 2; + ServiceCreator.start("vm://:1", services); + + _logger.info("Starting " + ++run + " client..."); + + new Client("vm://:1", services).shutdownWhenComplete(); + + + _logger.info("Completed " + run + " successfully!"); + } + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(CombinedTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java new file mode 100644 index 0000000000..6593f7d86a --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.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.test.unit.client.forwardall; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; + +/** + * Declare a queue and bind it to amq.direct with a 'well known' routing key, + * register a consumer for this queue and send a response to every message received. + */ +public class Service implements MessageListener +{ + private final AMQConnection _connection; + private final AMQSession _session; + + Service(String broker) throws Exception + { + this(connect(broker)); + } + + Service(AMQConnection connection) throws Exception + { + _connection = connection; + AMQQueue queue = new SpecialQueue(connection, "ServiceQueue"); + _session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _session.createConsumer(queue).setMessageListener(this); + _connection.start(); + } + + public void onMessage(Message request) + { + try + { + Message response = _session.createTextMessage("Response!"); + Destination replyTo = request.getJMSReplyTo(); + _session.createProducer(replyTo).send(response); + } + catch (Exception e) + { + e.printStackTrace(System.out); + } + } + + public void close() throws JMSException + { + _connection.close(); + } + + static AMQConnection connect(String broker) throws Exception + { + return new AMQConnection(broker, "guest", "guest", "Client" + System.currentTimeMillis(), "test"); + } + +// public static void main(String[] argv) throws Exception +// { +// String broker = argv.length == 0? "localhost:5672" : argv[0]; +// new Service(broker); +// } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java new file mode 100644 index 0000000000..be16f6b7ae --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.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.test.unit.client.forwardall; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; + +public class ServiceCreator implements Runnable +{ + private static final Logger _logger = LoggerFactory.getLogger(ServiceCreator.class); + + private static Thread[] threads; + private static ServiceCreator[] _services; + + private final String broker; + private Service service; + + ServiceCreator(String broker) + { + this.broker = broker; + } + + public void run() + { + try + { + service = new Service(broker); + } + catch (Exception e) + { + e.printStackTrace(System.out); + } + } + + public void closeSC() throws JMSException + { + service.close(); + } + + static void closeAll() + { + for (int i = 0; i < _services.length; i++) + { + try + { + _services[i].closeSC(); + } + catch (JMSException e) + { + // ignore + } + } + } + + static void start(String broker, int services) throws InterruptedException + { + threads = new Thread[services]; + _services = new ServiceCreator[services]; + ServiceCreator runner = new ServiceCreator(broker); + // start services + _logger.info("Starting " + services + " services..."); + for (int i = 0; i < services; i++) + { + threads[i] = new Thread(runner); + _services[i] = runner; + threads[i].start(); + } + + for (int i = 0; i < threads.length; i++) + { + threads[i].join(); + } + } + + public static void main(String[] argv) throws Exception + { + final String connectionString; + final int services; + if (argv.length == 0) + { + connectionString = "localhost:5672"; + services = 100; + } + else + { + connectionString = argv[0]; + services = Integer.parseInt(argv[1]); + } + + start(connectionString, services); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.java new file mode 100644 index 0000000000..27371b0397 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/SpecialQueue.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.test.unit.client.forwardall; + +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.framing.AMQShortString; + +/** + * Queue that allows several private queues to be registered and bound + * to an exchange with the same routing key. + * + */ +class SpecialQueue extends AMQQueue +{ + private final AMQShortString name; + + SpecialQueue(AMQConnection con, String name) + { + super(con, name, true); + this.name = new AMQShortString(name); + } + + public AMQShortString getRoutingKey() + { + return name; + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/BytesMessageTest.java new file mode 100644 index 0000000000..bbabf0b57d --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/MapMessageTest.java new file mode 100644 index 0000000000..3e04c36b38 --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java new file mode 100644 index 0000000000..cd03b523d1 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/ObjectMessageTest.java @@ -0,0 +1,345 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 junit.framework.TestCase; + +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.testutil.VMBrokerSetup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class ObjectMessageTest extends TestCase implements MessageListener +{ + private static final Logger _logger = LoggerFactory.getLogger(ObjectMessageTest.class); + + private AMQConnection connection; + private AMQDestination destination; + private AMQSession session; + private MessageProducer producer; + private Serializable[] data; + private volatile boolean waiting; + private int received; + private final ArrayList items = new ArrayList(); + + private String _broker = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + connection = new AMQConnection(_broker, "guest", "guest", randomize("Client"), "test"); + destination = new AMQQueue(connection, randomize("LatencyTest"), true); + session = (AMQSession) connection.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + // set up a consumer + session.createConsumer(destination).setMessageListener(this); + connection.start(); + + // create a publisher + producer = session.createProducer(destination, false, false, true); + A a1 = new A(1, "A"); + A a2 = new A(2, "a"); + B b = new B(1, "B"); + C c = new C(); + c.put("A1", a1); + c.put("a2", a2); + c.put("B", b); + c.put("String", "String"); + + data = new Serializable[] { a1, a2, b, c, "Hello World!", new Integer(1001) }; + } + + protected void tearDown() throws Exception + { + super.tearDown(); + } + + public ObjectMessageTest() + { } + + ObjectMessageTest(String broker) throws Exception + { + _broker = broker; + } + + public void testSendAndReceive() throws Exception + { + try + { + send(); + waitUntilReceived(data.length); + check(); + _logger.info("All " + data.length + " items matched."); + } + catch (Exception e) + { + e.printStackTrace(); + fail("This Test should succeed but failed due to: " + e); + } + finally + { + close(); + } + } + + public void testSetObjectPropertyForString() throws Exception + { + String testStringProperty = "TestStringProperty"; + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestStringProperty", testStringProperty); + assertEquals(testStringProperty, msg.getObjectProperty("TestStringProperty")); + } + + public void testSetObjectPropertyForBoolean() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestBooleanProperty", Boolean.TRUE); + assertEquals(Boolean.TRUE, msg.getObjectProperty("TestBooleanProperty")); + } + + public void testSetObjectPropertyForByte() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestByteProperty", Byte.MAX_VALUE); + assertEquals(Byte.MAX_VALUE, msg.getObjectProperty("TestByteProperty")); + } + + public void testSetObjectPropertyForShort() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestShortProperty", Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, msg.getObjectProperty("TestShortProperty")); + } + + public void testSetObjectPropertyForInteger() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestIntegerProperty", Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, msg.getObjectProperty("TestIntegerProperty")); + } + + public void testSetObjectPropertyForDouble() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestDoubleProperty", Double.MAX_VALUE); + assertEquals(Double.MAX_VALUE, msg.getObjectProperty("TestDoubleProperty")); + } + + public void testSetObjectPropertyForFloat() throws Exception + { + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestFloatProperty", Float.MAX_VALUE); + assertEquals(Float.MAX_VALUE, msg.getObjectProperty("TestFloatProperty")); + } + + public void testSetObjectPropertyForByteArray() throws Exception + { + byte[] array = { 1, 2, 3, 4, 5 }; + ObjectMessage msg = session.createObjectMessage(data[0]); + msg.setObjectProperty("TestByteArrayProperty", array); + assertTrue(Arrays.equals(array, (byte[]) msg.getObjectProperty("TestByteArrayProperty"))); + } + + public void testSetObjectForNull() throws Exception + { + ObjectMessage msg = session.createObjectMessage(); + msg.setObject(null); + assertNull(msg.getObject()); + } + + private void send() throws Exception + { + for (int i = 0; i < data.length; i++) + { + ObjectMessage msg; + if ((i % 2) == 0) + { + msg = session.createObjectMessage(data[i]); + } + else + { + msg = session.createObjectMessage(); + msg.setObject(data[i]); + } + + producer.send(msg); + } + } + + public void check() throws Exception + { + Object[] actual = (Object[]) items.toArray(); + if (actual.length != data.length) + { + throw new Exception("Expected " + data.length + " objects, got " + actual.length); + } + + for (int i = 0; i < data.length; i++) + { + if (actual[i] instanceof Exception) + { + throw new Exception("Error on receive of " + data[i], ((Exception) actual[i])); + } + + if (actual[i] == null) + { + throw new Exception("Expected " + data[i] + " got null"); + } + + if (!data[i].equals(actual[i])) + { + throw new Exception("Expected " + data[i] + " got " + actual[i]); + } + } + } + + private void close() throws Exception + { + session.close(); + connection.close(); + } + + private synchronized void waitUntilReceived(int count) throws InterruptedException + { + waiting = true; + while (received < count) + { + wait(); + } + + waiting = false; + } + + public void onMessage(Message message) + { + + try + { + if (message instanceof ObjectMessage) + { + items.add(((ObjectMessage) message).getObject()); + } + else + { + _logger.error("ERROR: Got " + message.getClass().getName() + " not ObjectMessage"); + items.add(message); + } + } + catch (JMSException e) + { + e.printStackTrace(); + items.add(e); + } + + synchronized (this) + { + received++; + notify(); + } + } + + public static void main(String[] argv) throws Exception + { + String broker = (argv.length > 0) ? argv[0] : "vm://:1"; + if ("-help".equals(broker)) + { + System.out.println("Usage: "); + } + + new ObjectMessageTest(broker).testSendAndReceive(); + } + + private static class A implements Serializable + { + private String sValue; + private int iValue; + + A(int i, String s) + { + sValue = s; + iValue = i; + } + + public int hashCode() + { + return iValue; + } + + public boolean equals(Object o) + { + return (o instanceof A) && equals((A) o); + } + + protected boolean equals(A a) + { + return areEqual(a.sValue, sValue) && (a.iValue == iValue); + } + } + + private static class B extends A + { + private long time; + + B(int i, String s) + { + super(i, s); + time = System.currentTimeMillis(); + } + + protected boolean equals(A a) + { + return super.equals(a) && (a instanceof B) && (time == ((B) a).time); + } + } + + private static class C extends HashMap implements Serializable + { } + + private static boolean areEqual(Object a, Object b) + { + return (a == null) ? (b == null) : a.equals(b); + } + + private static String randomize(String in) + { + return in + System.currentTimeMillis(); + } + + public static junit.framework.Test suite() + { + return new VMBrokerSetup(new junit.framework.TestSuite(ObjectMessageTest.class)); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/StreamMessageTest.java new file mode 100644 index 0000000000..802f1e6c2e --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/message/TextMessageTest.java new file mode 100644 index 0000000000..30f3b0b4eb --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.java new file mode 100644 index 0000000000..d14e0b771f --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/protocol/TestIoSession.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.test.unit.client.protocol; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoService; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.IoSessionConfig; +import org.apache.mina.common.TransportType; +import org.apache.mina.common.support.BaseIoSession; + +public class TestIoSession extends BaseIoSession { + + private String _stringLocalAddress; + private int _localPort; + + public SocketAddress getLocalAddress() + { + //create a new address for testing purposes using member variables + return new InetSocketAddress(_stringLocalAddress,_localPort); + } + + protected void updateTrafficMask() { + //dummy + } + + public IoService getService() { + return null; + } + + public IoServiceConfig getServiceConfig() { + return null; + } + + public IoHandler getHandler() { + return null; + } + + public IoSessionConfig getConfig() { + return null; + } + + public IoFilterChain getFilterChain() { + return null; + } + + public TransportType getTransportType() { + return null; + } + + public SocketAddress getRemoteAddress() { + return null; + } + + public SocketAddress getServiceAddress() { + return null; + } + + public int getScheduledWriteRequests() { + return 0; + } + + public int getScheduledWriteBytes() { + return 0; + } + + public String getStringLocalAddress() { + return _stringLocalAddress; + } + + public void setStringLocalAddress(String _stringLocalAddress) { + this._stringLocalAddress = _stringLocalAddress; + } + + public int getLocalPort() { + return _localPort; + } + + public void setLocalPort(int _localPort) { + this._localPort = _localPort; + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.java new file mode 100644 index 0000000000..241c9177a8 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/client/temporaryqueue/TemporaryQueueTest.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.test.unit.client.temporaryqueue; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TemporaryQueue; +import javax.jms.TextMessage; +import javax.jms.Queue; + +import junit.framework.TestCase; +import junit.framework.Assert; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.URLSyntaxException; + +import java.util.List; +import java.util.LinkedList; + +public class TemporaryQueueTest extends TestCase +{ + + String _broker = "vm://:1"; + + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + TransportConnection.killAllVMBrokers(); + } + + protected Connection createConnection() throws AMQException, URLSyntaxException + { + return new AMQConnection(_broker, "guest", "guest", + "fred", "test"); + } + + public void testTempoaryQueue() throws Exception + { + Connection conn = createConnection(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = session.createTemporaryQueue(); + assertNotNull(queue); + MessageProducer producer = session.createProducer(queue); + MessageConsumer consumer = session.createConsumer(queue); + conn.start(); + producer.send(session.createTextMessage("hello")); + TextMessage tm = (TextMessage) consumer.receive(2000); + assertNotNull(tm); + assertEquals("hello", tm.getText()); + + try + { + queue.delete(); + fail("Expected JMSException : should not be able to delete while there are active consumers"); + } + catch (JMSException je) + { + ; //pass + } + + consumer.close(); + + try + { + queue.delete(); + } + catch (JMSException je) + { + fail("Unexpected Exception: " + je.getMessage()); + } + + conn.close(); + } + + public void tUniqueness() throws JMSException, AMQException, URLSyntaxException + { + int numProcs = Runtime.getRuntime().availableProcessors(); + final int threadsProc = 5; + + runUniqueness(1, 10); + runUniqueness(numProcs * threadsProc, 10); + runUniqueness(numProcs * threadsProc, 100); + runUniqueness(numProcs * threadsProc, 500); + } + + void runUniqueness(int makers, int queues) throws JMSException, AMQException, URLSyntaxException + { + Connection connection = createConnection(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + List tqList = new LinkedList(); + + //Create Makers + for (int m = 0; m < makers; m++) + { + tqList.add(new TempQueueMaker(session, queues)); + } + + + List threadList = new LinkedList(); + + //Create Makers + for (TempQueueMaker maker : tqList) + { + threadList.add(new Thread(maker)); + } + + //Start threads + for (Thread thread : threadList) + { + thread.start(); + } + + // Join Threads + for (Thread thread : threadList) + { + try + { + thread.join(); + } + catch (InterruptedException e) + { + fail("Couldn't correctly join threads"); + } + } + + + List list = new LinkedList(); + + // Test values + for (TempQueueMaker maker : tqList) + { + check(maker, list); + } + + Assert.assertEquals("Not enough queues made.", makers * queues, list.size()); + + connection.close(); + } + + private void check(TempQueueMaker tq, List list) + { + for (AMQQueue q : tq.getList()) + { + if (list.contains(q)) + { + fail(q + " already exists."); + } + else + { + list.add(q); + } + } + } + + + class TempQueueMaker implements Runnable + { + List _queues; + Session _session; + private int _count; + + + TempQueueMaker(Session session, int queues) throws JMSException + { + _queues = new LinkedList(); + + _count = queues; + + _session = session; + } + + public void run() + { + int i = 0; + try + { + for (; i < _count; i++) + { + _queues.add((AMQQueue) _session.createTemporaryQueue()); + } + } + catch (JMSException jmse) + { + //stop + } + } + + List getList() + { + return _queues; + } + } + + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TemporaryQueueTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java new file mode 100644 index 0000000000..5a61480f6a --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/CloseBeforeAckTest.java @@ -0,0 +1,147 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.close; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.transport.TransportConnection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import uk.co.thebadgerset.junit.concurrency.TestRunnable; +import uk.co.thebadgerset.junit.concurrency.ThreadTestCoordinator; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; + +/** + * This test forces the situation where a session is closed whilst a message consumer is still in its onMessage method. + * Running in AUTO_ACK mode, the close call ought to wait until the onMessage method completes, and the ack is sent + * before closing the connection. + * + *

    CRC Card
    Responsibilities Collaborations
    Check that + * closing a connection whilst handling a message, blocks till completion of the handler.
    + */ +public class CloseBeforeAckTest extends TestCase +{ + private static final Logger log = LoggerFactory.getLogger(CloseBeforeAckTest.class); + + Connection connection; + Session session; + public static final String TEST_QUEUE_NAME = "TestQueue"; + private int TEST_COUNT = 25; + + class TestThread1 extends TestRunnable implements MessageListener + { + public void runWithExceptions() throws Exception + { + // Set this up to listen for message on the test session. + session.createConsumer(session.createQueue(TEST_QUEUE_NAME)).setMessageListener(this); + } + + public void onMessage(Message message) + { + // Give thread 2 permission to close the session. + allow(new int[] { 1 }); + + // Wait until thread 2 has closed the connection, or is blocked waiting for this to complete. + waitFor(new int[] { 1 }, true); + } + } + + TestThread1 testThread1 = new TestThread1(); + + TestRunnable testThread2 = + new TestRunnable() + { + public void runWithExceptions() throws Exception + { + // Send a message to be picked up by thread 1. + session.createProducer(null).send(session.createQueue(TEST_QUEUE_NAME), + session.createTextMessage("Hi there thread 1!")); + + // Wait for thread 1 to pick up the message and give permission to continue. + waitFor(new int[] { 0 }, false); + + // Close the connection. + session.close(); + + // Allow thread 1 to continue to completion, if it is erronously still waiting. + allow(new int[] { 1 }); + } + }; + + public void testCloseBeforeAutoAck_QPID_397() throws Exception + { + // Create a session in auto acknowledge mode. This problem shows up in auto acknowledge if the client acks + // message at the end of the onMessage method, after a close has been sent. + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ThreadTestCoordinator tt = new ThreadTestCoordinator(2); + + tt.addTestThread(testThread1, 0); + tt.addTestThread(testThread2, 1); + tt.setDeadlockTimeout(500); + tt.run(); + + String errorMessage = tt.joinAndRetrieveMessages(); + + // Print any error messages or exceptions. + log.debug(errorMessage); + + if (!tt.getExceptions().isEmpty()) + { + for (Exception e : tt.getExceptions()) + { + log.debug("Exception thrown during test thread: ", e); + } + } + + Assert.assertTrue(errorMessage, "".equals(errorMessage)); + } + + public void closeBeforeAutoAckManyTimes() throws Exception + { + for (int i = 0; i < TEST_COUNT; i++) + { + testCloseBeforeAutoAck_QPID_397(); + } + } + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + connection = new AMQConnection("vm://:1", "guest", "guest", getName(), "test"); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killVMBroker(1); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java new file mode 100644 index 0000000000..bf8802c803 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java @@ -0,0 +1,389 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.close; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.testutil.QpidClientConnection; +import org.apache.qpid.url.URLSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.UUID; + +public class MessageRequeueTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(MessageRequeueTest.class); + + protected static AtomicInteger consumerIds = new AtomicInteger(0); + protected final Integer numTestMessages = 150; + + protected final int consumeTimeout = 3000; + + + protected String payload = "Message:"; + + protected final String BROKER = "vm://:1"; + private boolean testReception = true; + + private long[] receieved = new long[numTestMessages + 1]; + private boolean passed = false; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + + + } + + protected void tearDown() throws Exception + { + super.tearDown(); + + + TransportConnection.killVMBroker(1); + } + + /** + * multiple consumers + * + * @throws javax.jms.JMSException if a JMS problem occurs + * @throws InterruptedException on timeout + */ + public void testDrain() throws JMSException, InterruptedException + { + final String queueName = "direct://amq.direct//queue" + UUID.randomUUID().toString(); + + QpidClientConnection conn = new QpidClientConnection(BROKER); + + conn.connect(); + // clear queue + conn.consume(queueName, consumeTimeout); + // load test data + _logger.info("creating test data, " + numTestMessages + " messages"); + conn.put(queueName, payload, numTestMessages); + // close this connection + conn.disconnect(); + + conn = new QpidClientConnection(BROKER); + + conn.connect(); + + _logger.info("consuming queue " + queueName); + Queue q = conn.getSession().createQueue(queueName); + + final MessageConsumer consumer = conn.getSession().createConsumer(q); + int messagesReceived = 0; + + long[] messageLog = new long[numTestMessages + 1]; + + _logger.info("consuming..."); + Message msg = consumer.receive(1000); + while (msg != null) + { + messagesReceived++; + + long dt = ((AbstractJMSMessage) msg).getDeliveryTag(); + + int msgindex = msg.getIntProperty("index"); + if (messageLog[msgindex] != 0) + { + _logger.error("Received Message(" + msgindex + ":" + ((AbstractJMSMessage) msg).getDeliveryTag() + + ") more than once."); + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt + "IN:" + msgindex); + } + + if (dt == 0) + { + _logger.error("DT is zero for msg:" + msgindex); + } + + messageLog[msgindex] = dt; + + // get Next message + msg = consumer.receive(1000); + } + + conn.getSession().commit(); + consumer.close(); + assertEquals("number of consumed messages does not match initial data", (int) numTestMessages, messagesReceived); + + int index = 0; + StringBuilder list = new StringBuilder(); + list.append("Failed to receive:"); + int failed = 0; + + for (long b : messageLog) + { + if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist + { + _logger.error("Index: " + index + " was not received."); + list.append(" "); + list.append(index); + list.append(":"); + list.append(b); + failed++; + } + + index++; + } + + assertEquals(list.toString(), 0, failed); + _logger.info("consumed: " + messagesReceived); + conn.disconnect(); + passed = true; + + } + + + + /** multiple consumers + * Based on code subbmitted by client FT-304 + */ + public void testCompetingConsumers() throws JMSException, InterruptedException + { + final String queueName = "direct://amq.direct//queue" + UUID.randomUUID().toString(); + + QpidClientConnection conn = new QpidClientConnection(BROKER); + + conn.connect(); + // clear queue + conn.consume(queueName, consumeTimeout); + // load test data + _logger.info("creating test data, " + numTestMessages + " messages"); + conn.put(queueName, payload, numTestMessages); + // close this connection + conn.disconnect(); + + Consumer c1 = new Consumer(queueName); + Consumer c2 = new Consumer(queueName); + Consumer c3 = new Consumer(queueName); + Consumer c4 = new Consumer(queueName); + + Thread t1 = new Thread(c1); + Thread t2 = new Thread(c2); + Thread t3 = new Thread(c3); + Thread t4 = new Thread(c4); + + t1.start(); + t2.start(); + t3.start(); + t4.start(); + + try + { + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } + catch (InterruptedException e) + { + fail("Uanble to join to Consumer theads"); + } + + _logger.info("consumer 1 count is " + c1.getCount()); + _logger.info("consumer 2 count is " + c2.getCount()); + _logger.info("consumer 3 count is " + c3.getCount()); + _logger.info("consumer 4 count is " + c4.getCount()); + + Integer totalConsumed = c1.getCount() + c2.getCount() + c3.getCount() + c4.getCount(); + + // Check all messages were correctly delivered + int index = 0; + StringBuilder list = new StringBuilder(); + list.append("Failed to receive:"); + int failed = 0; + + for (long b : receieved) + { + if ((b == 0) && (index != 0)) // delivery tag of zero shouldn't exist (and we don't have msg 0) + { + fail("Index: " + index + " was not received."); + list.append(" "); + list.append(index); + list.append(":"); + list.append(b); + failed++; + } + + index++; + } + + assertEquals(list.toString() + "-" + numTestMessages + "-" + totalConsumed, 0, failed); + assertTrue("number of consumed messages does not match initial data: " + totalConsumed, numTestMessages <= totalConsumed); + + } + + class Consumer implements Runnable + { + private Integer count = 0; + private Integer id; + private final String _queueName; + + public Consumer(String queueName) + { + _queueName = queueName; + id = consumerIds.addAndGet(1); + } + + public void run() + { + try + { + _logger.info("consumer-" + id + ": starting"); + QpidClientConnection conn = new QpidClientConnection(BROKER); + + conn.connect(); + + _logger.info("consumer-" + id + ": connected, consuming..."); + Message result; + do + { + result = conn.getNextMessage(_queueName, consumeTimeout); + if (result != null) + { + + long dt = ((AbstractJMSMessage) result).getDeliveryTag(); + + if (testReception) + { + int msgindex = result.getIntProperty("index"); + if (receieved[msgindex] != 0) + { + _logger.error("Received Message(" + msgindex + ":" + + ((AbstractJMSMessage) result).getDeliveryTag() + ") more than once."); + } + + if (_logger.isInfoEnabled()) + { + _logger.info("Received Message(" + System.identityHashCode(msgindex) + ") " + "DT:" + dt + + "IN:" + msgindex); + } + + if (dt == 0) + { + _logger.error("DT is zero for msg:" + msgindex); + } + + receieved[msgindex] = dt; + } + + count++; + if ((count % 100) == 0) + { + _logger.info("consumer-" + id + ": got " + result + ", new count is " + count); + } + } + } + while (result != null); + + _logger.info("consumer-" + id + ": complete"); + conn.disconnect(); + + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public Integer getCount() + { + return count; + } + + public Integer getId() + { + return id; + } + } + + public void testRequeue() throws JMSException, AMQException, URLSyntaxException, InterruptedException + { + final String queue = "direct://amq.direct//queue" + UUID.randomUUID().toString(); + + QpidClientConnection conn = new QpidClientConnection(BROKER); + + conn.connect(); + // clear queue + conn.consume(queue, consumeTimeout); + // load test data + _logger.info("creating test data, " + numTestMessages + " messages"); + conn.put(queue, payload, numTestMessages); + // close this connection + conn.disconnect(); + + int run = 0; + // while (run < 10) + { + run++; + + if (_logger.isInfoEnabled()) + { + _logger.info("testRequeue run " + run); + } + + String virtualHost = "/test"; + String brokerlist = BROKER; + String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'"; + + AMQConnection amqConn = new AMQConnection(brokerUrl); + Session session = amqConn.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue q = session.createQueue(queue); + + _logger.debug("Create Consumer"); + MessageConsumer consumer = session.createConsumer(q); + + amqConn.start(); + + _logger.debug("Receiving msg"); + Message msg = consumer.receive(2000); + + assertNotNull("Message should not be null", msg); + + // As we have not ack'd message will be requeued. + _logger.debug("Close Consumer"); + consumer.close(); + + _logger.debug("Close Connection"); + amqConn.close(); + } + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.java new file mode 100644 index 0000000000..5e2703d5a5 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/close/TopicPublisherCloseTest.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.test.unit.close; + +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; + +/** + * @author Apache Software Foundation + */ +public class TopicPublisherCloseTest extends TestCase +{ + + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testAllMethodsThrowAfterConnectionClose() throws Exception + { + AMQConnection connection = new AMQConnection(_connectionString, "guest", "guest", "Client", "test"); + + Topic destination1 = new AMQTopic(connection, "t1"); + TopicSession session1 = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TopicPublisher pub = session1.createPublisher(destination1); + connection.close(); + try + { + pub.getDeliveryMode(); + fail("Expected exception not thrown"); + } + catch (javax.jms.IllegalStateException e) + { + // PASS + } + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.java new file mode 100644 index 0000000000..a1d9af558c --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSDestinationTest.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.test.unit.message; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @author Apache Software Foundation + */ +public class JMSDestinationTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(JMSDestinationTest.class); + + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testJMSDestination() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false, + true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + + TextMessage sentMsg = producerSession.createTextMessage("hello"); + assertNull(sentMsg.getJMSDestination()); + + producer.send(sentMsg); + + assertEquals(sentMsg.getJMSDestination(), queue); + + con2.close(); + + con.start(); + + TextMessage rm = (TextMessage) consumer.receive(); + assertNotNull(rm); + + assertEquals(rm.getJMSDestination(), queue); + con.close(); + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.java new file mode 100644 index 0000000000..3012909daa --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/JMSPropertiesTest.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.test.unit.message; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.message.NonQpidObjectMessage; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * @author Apache Software Foundation + */ +public class JMSPropertiesTest extends TestCase +{ + + private static final Logger _logger = LoggerFactory.getLogger(JMSPropertiesTest.class); + + public String _connectionString = "vm://:1"; + + 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"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testJMSProperties() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = + new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("someQ"), new AMQShortString("someQ"), false, + true); + MessageConsumer consumer = consumerSession.createConsumer(queue); + + AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(queue); + Destination JMS_REPLY_TO = new AMQQueue(con2, "my.replyto"); + // create a test message to send + ObjectMessage sentMsg = new NonQpidObjectMessage(); + sentMsg.setJMSCorrelationID(JMS_CORR_ID); + sentMsg.setJMSDeliveryMode(JMS_DELIV_MODE); + sentMsg.setJMSType(JMS_TYPE); + sentMsg.setJMSReplyTo(JMS_REPLY_TO); + + // send it + producer.send(sentMsg); + + con2.close(); + + con.start(); + + // get message and check JMS properties + ObjectMessage rm = (ObjectMessage) consumer.receive(); + assertNotNull(rm); + + assertEquals("JMS Correlation ID mismatch", sentMsg.getJMSCorrelationID(), rm.getJMSCorrelationID()); + // TODO: Commented out as always overwritten by send delivery mode value - prob should not set in conversion + // assertEquals("JMS Delivery Mode mismatch",sentMsg.getJMSDeliveryMode(),rm.getJMSDeliveryMode()); + assertEquals("JMS Type mismatch", sentMsg.getJMSType(), rm.getJMSType()); + assertEquals("JMS Reply To mismatch", sentMsg.getJMSReplyTo(), rm.getJMSReplyTo()); + + con.close(); + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.java new file mode 100644 index 0000000000..fd425b9930 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/MessageConverterTest.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.test.unit.message; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.JMSMapMessage; +import org.apache.qpid.client.message.JMSTextMessage; +import org.apache.qpid.client.message.MessageConverter; +import org.apache.qpid.exchange.ExchangeDefaults; + + +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; + + protected void setUp() throws Exception + { + super.setUp(); + testTextMessage = new JMSTextMessage(); + + //Set Message Text + testTextMessage.setText("testTextMessage text"); + setMessageProperties(testTextMessage); + + testMapMessage = new JMSMapMessage(); + testMapMessage.setString("testMapString", "testMapStringValue"); + testMapMessage.setDouble("testMapDouble", Double.MAX_VALUE); + } + + public void testSetProperties() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter((TextMessage) testTextMessage).getConvertedMessage(); + mesagePropertiesTest(testTextMessage, newMessage); + } + + public void testJMSTextMessageConversion() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter((TextMessage) testTextMessage).getConvertedMessage(); + assertEquals("Converted message text mismatch", ((JMSTextMessage) newMessage).getText(), testTextMessage.getText()); + } + + public void testJMSMapMessageConversion() throws Exception + { + AbstractJMSMessage newMessage = new MessageConverter((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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/NonQpidMessage.java new file mode 100644 index 0000000000..df53c796b2 --- /dev/null +++ b/Final2/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/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java new file mode 100644 index 0000000000..9c4f2af107 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/message/StreamMessageTest.java @@ -0,0 +1,160 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.message; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQHeadersExchange; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.url.AMQBindingURL; +import org.apache.qpid.url.BindingURL; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageEOFException; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.StreamMessage; + +/** + * @author Apache Software Foundation + */ +public class StreamMessageTest extends TestCase +{ + + private static final Logger _logger = LoggerFactory.getLogger(StreamMessageTest.class); + + public String _connectionString = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testStreamMessageEOF() throws Exception + { + Connection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + AMQHeadersExchange queue = + new AMQHeadersExchange(new AMQBindingURL( + ExchangeDefaults.HEADERS_EXCHANGE_CLASS + "://" + ExchangeDefaults.HEADERS_EXCHANGE_NAME + + "/test/queue1?" + BindingURL.OPTION_ROUTING_KEY + "='F0000=1'")); + FieldTable ft = new FieldTable(); + ft.setString("F1000", "1"); + MessageConsumer consumer = + consumerSession.createConsumer(queue, AMQSession.DEFAULT_PREFETCH_LOW_MARK, + AMQSession.DEFAULT_PREFETCH_HIGH_MARK, false, false, (String) null, ft); + + // force synch to ensure the consumer has resulted in a bound queue + // ((AMQSession) consumerSession).declareExchangeSynch(ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS); + // This is the default now + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Need to start the "producer" connection in order to receive bounced messages + _logger.info("Starting producer connection"); + con2.start(); + + MessageProducer mandatoryProducer = producerSession.createProducer(queue); + + // Third test - should be routed + _logger.info("Sending isBound message"); + StreamMessage msg = producerSession.createStreamMessage(); + + msg.setStringProperty("F1000", "1"); + + msg.writeByte((byte) 42); + + mandatoryProducer.send(msg); + + _logger.info("Starting consumer connection"); + con.start(); + + StreamMessage msg2 = (StreamMessage) consumer.receive(); + + msg2.readByte(); + try + { + msg2.readByte(); + } + catch (Exception e) + { + assertTrue("Expected MessageEOFException: " + e, e instanceof MessageEOFException); + } + } + + public void testModifyReceivedMessageExpandsBuffer() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + AMQSession consumerSession = (AMQSession) con.createSession(false, Session.CLIENT_ACKNOWLEDGE); + AMQQueue queue = new AMQQueue(con.getDefaultQueueExchangeName(), new AMQShortString("testQ")); + MessageConsumer consumer = consumerSession.createConsumer(queue); + consumer.setMessageListener(new MessageListener() + { + + public void onMessage(Message message) + { + StreamMessage sm = (StreamMessage) message; + try + { + sm.clearBody(); + sm.writeString("dfgjshfslfjshflsjfdlsjfhdsljkfhdsljkfhsd"); + } + catch (JMSException e) + { + _logger.error("Error when writing large string to received msg: " + e, e); + fail("Error when writing large string to received msg" + e); + } + } + }); + + Connection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + AMQSession producerSession = (AMQSession) con2.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageProducer mandatoryProducer = producerSession.createProducer(queue); + con.start(); + StreamMessage sm = producerSession.createStreamMessage(); + sm.writeInt(42); + mandatoryProducer.send(sm); + Thread.sleep(2000); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java new file mode 100644 index 0000000000..a0a8eb10ed --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -0,0 +1,192 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.topic; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.URLSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.TopicSubscriber; + +public class DurableSubscriptionTest extends TestCase +{ + private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class); + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testUnsubscribe() throws AMQException, JMSException, URLSyntaxException + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con, "MyTopic"); + _logger.info("Create Session 1"); + Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _logger.info("Create Consumer on Session 1"); + MessageConsumer consumer1 = session1.createConsumer(topic); + _logger.info("Create Producer on Session 1"); + MessageProducer producer = session1.createProducer(topic); + + _logger.info("Create Session 2"); + Session session2 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _logger.info("Create Durable Subscriber on Session 2"); + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + + _logger.info("Starting connection"); + con.start(); + + _logger.info("Producer sending message A"); + producer.send(session1.createTextMessage("A")); + + Message msg; + _logger.info("Receive message on consumer 1:expecting A"); + msg = consumer1.receive(); + assertEquals("A", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 1 :expecting null"); + msg = consumer1.receive(1000); + assertEquals(null, msg); + + _logger.info("Receive message on consumer 1:expecting A"); + msg = consumer2.receive(); + assertEquals("A", ((TextMessage) msg).getText()); + msg = consumer2.receive(1000); + _logger.info("Receive message on consumer 1 :expecting null"); + assertEquals(null, msg); + + _logger.info("Unsubscribe session2/consumer2"); + session2.unsubscribe("MySubscription"); + + _logger.info("Producer sending message B"); + producer.send(session1.createTextMessage("B")); + + _logger.info("Receive message on consumer 1 :expecting B"); + msg = consumer1.receive(); + assertEquals("B", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 1 :expecting null"); + msg = consumer1.receive(1000); + assertEquals(null, msg); + + _logger.info("Receive message on consumer 2 :expecting null"); + msg = consumer2.receive(1000); + assertEquals(null, msg); + + _logger.info("Close connection"); + con.close(); + } + + public void testDurabilityNOACK() throws AMQException, JMSException, URLSyntaxException + { + durabilityImpl(AMQSession.NO_ACKNOWLEDGE); + } + + public void testDurabilityAUTOACK() throws AMQException, JMSException, URLSyntaxException + { + durabilityImpl(Session.AUTO_ACKNOWLEDGE); + } + + private void durabilityImpl(int ackMode) throws AMQException, JMSException, URLSyntaxException + { + + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con, "MyTopic"); + Session session1 = con.createSession(false, ackMode); + MessageConsumer consumer1 = session1.createConsumer(topic); + + Session sessionProd = con.createSession(false, ackMode); + MessageProducer producer = sessionProd.createProducer(topic); + + Session session2 = con.createSession(false, ackMode); + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + + con.start(); + + producer.send(session1.createTextMessage("A")); + + Message msg; + msg = consumer1.receive(500); + assertNotNull("Message should be available", msg); + assertEquals("Message Text doesn't match", "A", ((TextMessage) msg).getText()); + + msg = consumer1.receive(500); + assertNull("There should be no more messages for consumption on consumer1.", msg); + + msg = consumer2.receive(); + assertNotNull(msg); + assertEquals("Consumer 2 should also received the first msg.", "A", ((TextMessage) msg).getText()); + msg = consumer2.receive(500); + assertNull("There should be no more messages for consumption on consumer2.", msg); + + consumer2.close(); + + Session session3 = con.createSession(false, ackMode); + MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "MySubscription"); + + producer.send(session1.createTextMessage("B")); + + _logger.info("Receive message on consumer 1 :expecting B"); + msg = consumer1.receive(500); + assertNotNull("Consumer 1 should get message 'B'.", msg); + assertEquals("Incorrect Message recevied on consumer1.", "B", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 1 :expecting null"); + msg = consumer1.receive(500); + assertNull("There should be no more messages for consumption on consumer1.", msg); + + _logger.info("Receive message on consumer 3 :expecting B"); + msg = consumer3.receive(500); + assertNotNull("Consumer 3 should get message 'B'.", msg); + assertEquals("Incorrect Message recevied on consumer4.", "B", ((TextMessage) msg).getText()); + _logger.info("Receive message on consumer 3 :expecting null"); + msg = consumer3.receive(500); + assertNull("There should be no more messages for consumption on consumer3.", msg); + + consumer1.close(); + consumer3.close(); + + con.close(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(DurableSubscriptionTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java new file mode 100644 index 0000000000..929e2799a9 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicPublisherTest.java @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.topic; + +import javax.jms.MessageConsumer; +import javax.jms.TextMessage; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; + +/** + * @author Apache Software Foundation + */ +public class TopicPublisherTest extends TestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + public void testUnidentifiedProducer() throws Exception + { + + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con,"MyTopic"); + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicPublisher publisher = session1.createPublisher(null); + MessageConsumer consumer1 = session1.createConsumer(topic); + con.start(); + publisher.publish(topic, session1.createTextMessage("Hello")); + TextMessage m = (TextMessage) consumer1.receive(2000); + assertNotNull(m); + try + { + publisher.publish(session1.createTextMessage("Goodbye")); + fail("Did not throw UnsupportedOperationException"); + } + catch (UnsupportedOperationException e) + { + // PASS + } + + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TopicPublisherTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java new file mode 100644 index 0000000000..065b06a87d --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java @@ -0,0 +1,375 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.topic; + +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.client.transport.TransportConnection; + + +/** @author Apache Software Foundation */ +public class TopicSessionTest extends TestCase +{ + private static final String BROKER = "vm://:1"; + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + TransportConnection.killAllVMBrokers(); + } + + + public void testTopicSubscriptionUnsubscription() throws Exception + { + + AMQConnection con = new AMQConnection(BROKER+"?retries='0'", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con.getDefaultTopicExchangeName(), "MyTopic"); + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0"); + TopicPublisher publisher = session1.createPublisher(topic); + + con.start(); + + TextMessage tm = session1.createTextMessage("Hello"); + publisher.publish(tm); + + tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + + session1.unsubscribe("subscription0"); + + try + { + session1.unsubscribe("not a subscription"); + fail("expected InvalidDestinationException when unsubscribing from unknown subscription"); + } + catch (InvalidDestinationException e) + { + ; // PASS + } + catch (Exception e) + { + fail("expected InvalidDestinationException when unsubscribing from unknown subscription, got: " + e); + } + + con.close(); + } + + public void testSubscriptionNameReuseForDifferentTopicSingleConnection() throws Exception + { + subscriptionNameReuseForDifferentTopic(false); + } + + public void testSubscriptionNameReuseForDifferentTopicTwoConnections() throws Exception + { + subscriptionNameReuseForDifferentTopic(true); + } + + private void subscriptionNameReuseForDifferentTopic(boolean shutdown) throws Exception + { + AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con, "MyTopic1" + String.valueOf(shutdown)); + AMQTopic topic2 = new AMQTopic(con, "MyOtherTopic1" + String.valueOf(shutdown)); + + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session1.createDurableSubscriber(topic, "subscription0"); + TopicPublisher publisher = session1.createPublisher(null); + + con.start(); + + publisher.publish(topic, session1.createTextMessage("hello")); + TextMessage m = (TextMessage) sub.receive(2000); + assertNotNull(m); + + if (shutdown) + { + session1.close(); + con.close(); + con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + con.start(); + session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + publisher = session1.createPublisher(null); + } + TopicSubscriber sub2 = session1.createDurableSubscriber(topic2, "subscription0"); + publisher.publish(topic, session1.createTextMessage("hello")); + if (!shutdown) + { + m = (TextMessage) sub.receive(2000); + assertNull(m); + } + publisher.publish(topic2, session1.createTextMessage("goodbye")); + m = (TextMessage) sub2.receive(2000); + assertNotNull(m); + assertEquals("goodbye", m.getText()); + con.close(); + } + + public void testUnsubscriptionAfterConnectionClose() throws Exception + { + AMQConnection con1 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con1, "MyTopic3"); + + TopicSession session1 = con1.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicPublisher publisher = session1.createPublisher(topic); + + AMQConnection con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "test"); + TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber sub = session2.createDurableSubscriber(topic, "subscription0"); + + con2.start(); + + publisher.publish(session1.createTextMessage("Hello")); + TextMessage tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + con2.close(); + publisher.publish(session1.createTextMessage("Hello2")); + con2 = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test2", "test"); + session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + sub = session2.createDurableSubscriber(topic, "subscription0"); + con2.start(); + tm = (TextMessage) sub.receive(2000); + assertNotNull(tm); + assertEquals("Hello2", tm.getText()); + con1.close(); + con2.close(); + } + + public void testTextMessageCreation() throws Exception + { + + AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + AMQTopic topic = new AMQTopic(con, "MyTopic4"); + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicPublisher publisher = session1.createPublisher(topic); + MessageConsumer consumer1 = session1.createConsumer(topic); + con.start(); + TextMessage tm = session1.createTextMessage("Hello"); + publisher.publish(tm); + tm = (TextMessage) consumer1.receive(200000L); + assertNotNull(tm); + String msgText = tm.getText(); + assertEquals("Hello", msgText); + tm = session1.createTextMessage(); + msgText = tm.getText(); + assertNull(msgText); + publisher.publish(tm); + tm = (TextMessage) consumer1.receive(20000000L); + assertNotNull(tm); + msgText = tm.getText(); + assertNull(msgText); + tm.clearBody(); + tm.setText("Now we are not null"); + publisher.publish(tm); + tm = (TextMessage) consumer1.receive(2000); + assertNotNull(tm); + msgText = tm.getText(); + assertEquals("Now we are not null", msgText); + + tm = session1.createTextMessage(""); + msgText = tm.getText(); + assertEquals("Empty string not returned", "", msgText); + publisher.publish(tm); + tm = (TextMessage) consumer1.receive(2000); + assertNotNull(tm); + assertEquals("Empty string not returned", "", msgText); + con.close(); + } + + public void testSendingSameMessage() throws Exception + { + AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryTopic topic = session.createTemporaryTopic(); + assertNotNull(topic); + TopicPublisher producer = session.createPublisher(topic); + MessageConsumer consumer = session.createConsumer(topic); + conn.start(); + TextMessage sentMessage = session.createTextMessage("Test Message"); + producer.send(sentMessage); + TextMessage receivedMessage = (TextMessage) consumer.receive(2000); + assertNotNull(receivedMessage); + assertEquals(sentMessage.getText(), receivedMessage.getText()); + producer.send(sentMessage); + receivedMessage = (TextMessage) consumer.receive(2000); + assertNotNull(receivedMessage); + assertEquals(sentMessage.getText(), receivedMessage.getText()); + + conn.close(); + + } + + public void testTemporaryTopic() throws Exception + { + AMQConnection conn = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test"); + TopicSession session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryTopic topic = session.createTemporaryTopic(); + assertNotNull(topic); + TopicPublisher producer = session.createPublisher(topic); + MessageConsumer consumer = session.createConsumer(topic); + conn.start(); + producer.send(session.createTextMessage("hello")); + TextMessage tm = (TextMessage) consumer.receive(2000); + assertNotNull(tm); + assertEquals("hello", tm.getText()); + + try + { + topic.delete(); + fail("Expected JMSException : should not be able to delete while there are active consumers"); + } + catch (JMSException je) + { + ; //pass + } + + consumer.close(); + + try + { + topic.delete(); + } + catch (JMSException je) + { + fail("Unexpected Exception: " + je.getMessage()); + } + + TopicSession session2 = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + try + { + session2.createConsumer(topic); + fail("Expected a JMSException when subscribing to a temporary topic created on adifferent session"); + } + catch (JMSException je) + { + ; // pass + } + + + conn.close(); + } + + + public void testNoLocal() throws Exception + { + + AMQConnection con = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test", "test"); + + AMQTopic topic = new AMQTopic(con, "testNoLocal"); + + TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicSubscriber noLocal = session1.createDurableSubscriber(topic, "noLocal", "", true); + TopicSubscriber select = session1.createDurableSubscriber(topic, "select", "Selector = 'select'", false); + TopicSubscriber normal = session1.createDurableSubscriber(topic, "normal"); + + TopicPublisher publisher = session1.createPublisher(topic); + + con.start(); + TextMessage m; + TextMessage message; + + //send message to all consumers + publisher.publish(session1.createTextMessage("hello-new2")); + + //test normal subscriber gets message + m = (TextMessage) normal.receive(5000); + assertNotNull(m); + + //test selector subscriber doesn't message + m = (TextMessage) select.receive(2000); + assertNull(m); + + //test nolocal subscriber doesn't message + m = (TextMessage) noLocal.receive(2000); + if (m != null) + { + System.out.println("Message:" + m.getText()); + } + assertNull(m); + + //send message to all consumers + message = session1.createTextMessage("hello2"); + message.setStringProperty("Selector", "select"); + + publisher.publish(message); + + //test normal subscriber gets message + m = (TextMessage) normal.receive(5000); + assertNotNull(m); + + //test selector subscriber does get message + m = (TextMessage) select.receive(2000); + assertNotNull(m); + + //test nolocal subscriber doesn't message + m = (TextMessage) noLocal.receive(1000); + assertNull(m); + + AMQConnection con2 = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test2", "test"); + TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE); + TopicPublisher publisher2 = session2.createPublisher(topic); + + + message = session2.createTextMessage("hello2"); + message.setStringProperty("Selector", "select"); + + publisher2.publish(message); + + //test normal subscriber gets message + m = (TextMessage) normal.receive(2000); + assertNotNull(m); + + //test selector subscriber does get message + m = (TextMessage) select.receive(2000); + assertNotNull(m); + + //test nolocal subscriber does message + m = (TextMessage) noLocal.receive(2000); + assertNotNull(m); + + + con.close(); + con2.close(); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TopicSessionTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.java new file mode 100644 index 0000000000..224463a446 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/CommitRollbackTest.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.transacted; + +import junit.framework.TestCase; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.url.URLSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * This class tests a number of commits and roll back scenarios + * + * Assumptions; - Assumes empty Queue + */ +public class CommitRollbackTest extends TestCase +{ + protected AMQConnection conn; + protected String queue = "direct://amq.direct//Qpid.Client.Transacted.CommitRollback.queue"; + protected static int testMethod = 0; + protected String payload = "xyzzy"; + private Session _session; + private MessageProducer _publisher; + private Session _pubSession; + private MessageConsumer _consumer; + Queue _jmsQueue; + + private static final Logger _logger = LoggerFactory.getLogger(CommitRollbackTest.class); + private static final String BROKER = "vm://:1"; + private boolean _gotone = false; + private boolean _gottwo = false; + private boolean _gottwoRedelivered = false; + + protected void setUp() throws Exception + { + super.setUp(); + if (BROKER.startsWith("vm")) + { + TransportConnection.createVMBroker(1); + } + + testMethod++; + queue += testMethod; + + newConnection(); + } + + private void newConnection() throws AMQException, URLSyntaxException, JMSException + { + conn = new AMQConnection("amqp://guest:guest@client/test?brokerlist='" + BROKER + "'"); + + _session = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE); + + _jmsQueue = _session.createQueue(queue); + _consumer = _session.createConsumer(_jmsQueue); + + _pubSession = conn.createSession(true, Session.CLIENT_ACKNOWLEDGE); + + _publisher = _pubSession.createProducer(_pubSession.createQueue(queue)); + + conn.start(); + } + + protected void tearDown() throws Exception + { + super.tearDown(); + + conn.close(); + if (BROKER.startsWith("vm")) + { + TransportConnection.killVMBroker(1); + } + } + + /** + * PUT a text message, disconnect before commit, confirm it is gone. + * + * @throws Exception On error + */ + public void testPutThenDisconnect() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testPutThenDisconnect"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _logger.info("reconnecting without commit"); + conn.close(); + + newConnection(); + + _logger.info("receiving result"); + Message result = _consumer.receive(1000); + + // commit to ensure message is removed from queue + _session.commit(); + + assertNull("test message was put and disconnected before commit, but is still present", result); + } + + /** + * PUT a text message, disconnect before commit, confirm it is gone. + * + * @throws Exception On error + */ + public void testPutThenCloseDisconnect() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testPutThenDisconnect"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _logger.info("closing publisher without commit"); + _publisher.close(); + + _logger.info("reconnecting without commit"); + conn.close(); + + newConnection(); + + _logger.info("receiving result"); + Message result = _consumer.receive(1000); + + // commit to ensure message is removed from queue + _session.commit(); + + assertNull("test message was put and disconnected before commit, but is still present", result); + } + + /** + * PUT a text message, rollback, confirm message is gone. The consumer is on the same connection but different + * session as producer + * + * @throws Exception On error + */ + public void testPutThenRollback() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testPutThenRollback"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _logger.info("rolling back"); + _pubSession.rollback(); + + _logger.info("receiving result"); + Message result = _consumer.receive(1000); + + assertNull("test message was put and rolled back, but is still present", result); + } + + /** + * GET a text message, disconnect before commit, confirm it is still there. The consumer is on a new connection + * + * @throws Exception On error + */ + public void testGetThenDisconnect() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testGetThenDisconnect"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _pubSession.commit(); + + _logger.info("getting test message"); + + Message msg = _consumer.receive(5000); + assertNotNull("retrieved message is null", msg); + + _logger.info("closing connection"); + conn.close(); + + newConnection(); + + _logger.info("receiving result"); + Message result = _consumer.receive(5000); + + _session.commit(); + + assertNotNull("test message was consumed and disconnected before commit, but is gone", result); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText()); + } + + /** + * GET a text message, close consumer, disconnect before commit, confirm it is still there. The consumer is on the + * same connection but different session as producer + * + * @throws Exception On error + */ + public void testGetThenCloseDisconnect() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testGetThenCloseDisconnect"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _pubSession.commit(); + + _logger.info("getting test message"); + + Message msg = _consumer.receive(5000); + assertNotNull("retrieved message is null", msg); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText()); + + _logger.info("reconnecting without commit"); + _consumer.close(); + conn.close(); + + newConnection(); + + _logger.info("receiving result"); + Message result = _consumer.receive(5000); + + _session.commit(); + + assertNotNull("test message was consumed and disconnected before commit, but is gone", result); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText()); + } + + /** + * GET a text message, rollback, confirm it is still there. The consumer is on the same connection but differnt + * session to the producer + * + * @throws Exception On error + */ + public void testGetThenRollback() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testGetThenRollback"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _pubSession.commit(); + + _logger.info("getting test message"); + + Message msg = _consumer.receive(1000); + + assertNotNull("retrieved message is null", msg); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText()); + + _logger.info("rolling back"); + + _session.rollback(); + + _logger.info("receiving result"); + + Message result = _consumer.receive(1000); + + _session.commit(); + assertNotNull("test message was consumed and rolled back, but is gone", result); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText()); + assertTrue("Messasge is not marked as redelivered", result.getJMSRedelivered()); + } + + /** + * GET a text message, close message producer, rollback, confirm it is still there. The consumer is on the same + * connection but different session as producer + * + * @throws Exception On error + */ + public void testGetThenCloseRollback() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testGetThenCloseRollback"; + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _pubSession.commit(); + + _logger.info("getting test message"); + + Message msg = _consumer.receive(5000); + + assertNotNull("retrieved message is null", msg); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) msg).getText()); + + _logger.info("Closing consumer"); + _consumer.close(); + + _logger.info("rolling back"); + _session.rollback(); + + _logger.info("receiving result"); + + _consumer = _session.createConsumer(_jmsQueue); + + Message result = _consumer.receive(5000); + + _session.commit(); + assertNotNull("test message was consumed and rolled back, but is gone", result); + assertEquals("test message was correct message", MESSAGE_TEXT, ((TextMessage) result).getText()); + assertTrue("Messasge is not marked as redelivered", result.getJMSRedelivered()); + } + + /** + * Test that rolling back a session purges the dispatcher queue, and the messages arrive in the correct order + * + * @throws Exception On error + */ + public void testSend2ThenRollback() throws Exception + { + int run = 0; + while (run < 10) + { + run++; + _logger.info("Run:" + run); + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending two test messages"); + _publisher.send(_pubSession.createTextMessage("1")); + _publisher.send(_pubSession.createTextMessage("2")); + _pubSession.commit(); + + _logger.info("getting test message"); + assertEquals("1", ((TextMessage) _consumer.receive(1000)).getText()); + + _logger.info("rolling back"); + _session.rollback(); + + _logger.info("receiving result"); + Message result = _consumer.receive(5000); + + assertNotNull("test message was consumed and rolled back, but is gone", result); + + // Message Order is: + + // Send 1 , 2 + // Retrieve 1 and then rollback + // Receieve 1 (redelivered) , 2 (may or may not be redelivered??) + + verifyMessages(result); + + // Occassionally get message 2 first! +// assertEquals("Should get message one first", "1", ((TextMessage) result).getText()); +// assertTrue("Message is not marked as redelivered", result.getJMSRedelivered()); +// +// result = _consumer.receive(1000); +// assertEquals("Second message should be message 2", "2", ((TextMessage) result).getText()); +// assertTrue("Message is not marked as redelivered", result.getJMSRedelivered()); +// +// result = _consumer.receive(1000); +// assertNull("There should be no more messages", result); + + _session.commit(); + } + } + + private void verifyMessages(Message result) throws JMSException + { + + if (result == null) + { + assertTrue("Didn't receive redelivered message one", _gotone); + assertTrue("Didn't receive message two at all", _gottwo | _gottwoRedelivered); + _gotone = false; + _gottwo = false; + _gottwoRedelivered = false; + return; + } + + if (((TextMessage) result).getText().equals("1")) + { + _logger.info("Got 1 redelivered"); + assertTrue("Message is not marked as redelivered", result.getJMSRedelivered()); + _gotone = true; + + } + else + { + assertEquals("2", ((TextMessage) result).getText()); + + if (result.getJMSRedelivered()) + { + _logger.info("Got 2 redelivered, message was prefetched"); + _gottwoRedelivered = true; + + } + else + { + _logger.warn("Got 2, message prefetched wasn't cleared or messages was in transit when rollback occured"); + assertFalse("Already received message two", _gottwo); + assertFalse("Already received message redelivered two", _gottwoRedelivered); + + _gottwo = true; + } + } + + verifyMessages(_consumer.receive(1000)); + } + + /** + * This test sends two messages receives on of them but doesn't ack it. + * The consumer is then closed + * the first message should be returned as redelivered. + * the second message should be delivered normally. + * @throws Exception + */ + public void testSend2ThenCloseAfter1andTryAgain() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending two test messages"); + _publisher.send(_pubSession.createTextMessage("1")); + _publisher.send(_pubSession.createTextMessage("2")); + _pubSession.commit(); + + _logger.info("getting test message"); + Message result = _consumer.receive(5000); + + assertNotNull("Message received should not be null", result); + assertEquals("1", ((TextMessage) result).getText()); + assertTrue("Messasge is marked as redelivered" + result, !result.getJMSRedelivered()); + + _logger.info("Closing Consumer"); + _consumer.close(); + + _logger.info("Creating New consumer"); + _consumer = _session.createConsumer(_jmsQueue); + + _logger.info("receiving result"); + +// NOTE: Both msg 1 & 2 will be marked as redelivered as they have both will have been rejected. +// Only the occasion where it is not rejected will it mean it hasn't arrived at the client yet. + result = _consumer.receive(5000); + assertNotNull("test message was consumed and rolled back, but is gone", result); + +// The first message back will be either 1 or 2 being redelivered + if (result.getJMSRedelivered()) + { + assertTrue("Messasge is not marked as redelivered" + result, result.getJMSRedelivered()); + } + else // or it will be msg 2 arriving the first time due to latency. + { + _logger.info("Message 2 wasn't prefetched so wasn't rejected"); + assertEquals("2", ((TextMessage) result).getText()); + } + + Message result2 = _consumer.receive(5000); + assertNotNull("test message was consumed and rolled back, but is gone", result2); + + // if this is message 1 then it should be marked as redelivered + if("1".equals(((TextMessage) result2).getText())) + { + assertTrue("Messasge is not marked as redelivered" + result2, result2.getJMSRedelivered()); + } + + assertNotSame("Messages should not have the same content",((TextMessage) result2).getText(), ((TextMessage) result).getText() ); + + result = _consumer.receive(1000); + assertNull("test message should be null:" + result, result); + + _session.commit(); + + } + + public void testPutThenRollbackThenGet() throws Exception + { + assertTrue("session is not transacted", _session.getTransacted()); + assertTrue("session is not transacted", _pubSession.getTransacted()); + + _logger.info("sending test message"); + String MESSAGE_TEXT = "testPutThenRollbackThenGet"; + + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + _pubSession.commit(); + + assertNotNull(_consumer.receive(5000)); + + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _logger.info("rolling back"); + _pubSession.rollback(); + + _logger.info("receiving result"); + Message result = _consumer.receive(5000); + assertNull("test message was put and rolled back, but is still present", result); + + _publisher.send(_pubSession.createTextMessage(MESSAGE_TEXT)); + + _pubSession.commit(); + + assertNotNull(_consumer.receive(5000)); + + } + +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.java new file mode 100644 index 0000000000..678474a18b --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/test/unit/transacted/TransactedTest.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.test.unit.transacted; + +import junit.framework.TestCase; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.framing.AMQShortString; +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.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.TextMessage; + +public class TransactedTest extends TestCase +{ + private AMQQueue queue1; + private AMQQueue queue2; + + private AMQConnection con; + private Session session; + private MessageConsumer consumer1; + private MessageProducer producer2; + + private AMQConnection prepCon; + private Session prepSession; + private MessageProducer prepProducer1; + + private AMQConnection testCon; + private Session testSession; + private MessageConsumer testConsumer1; + private MessageConsumer testConsumer2; + private static final Logger _logger = LoggerFactory.getLogger(TransactedTest.class); + + protected void setUp() throws Exception + { + super.setUp(); + TransportConnection.createVMBroker(1); + _logger.info("Create Connection"); + con = new AMQConnection("vm://:1", "guest", "guest", "TransactedTest", "test"); + + _logger.info("Create Session"); + session = con.createSession(true, Session.SESSION_TRANSACTED); + _logger.info("Create Q1"); + queue1 = + new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q1"), new AMQShortString("Q1"), false, + true); + _logger.info("Create Q2"); + queue2 = new AMQQueue(session.getDefaultQueueExchangeName(), new AMQShortString("Q2"), false); + + _logger.info("Create Consumer of Q1"); + consumer1 = session.createConsumer(queue1); + // Dummy just to create the queue. + _logger.info("Create Consumer of Q2"); + MessageConsumer consumer2 = session.createConsumer(queue2); + _logger.info("Close Consumer of Q2"); + consumer2.close(); + + _logger.info("Create producer to Q2"); + producer2 = session.createProducer(queue2); + + _logger.info("Start Connection"); + con.start(); + + _logger.info("Create prep connection"); + prepCon = new AMQConnection("vm://:1", "guest", "guest", "PrepConnection", "test"); + + _logger.info("Create prep session"); + prepSession = prepCon.createSession(false, AMQSession.NO_ACKNOWLEDGE); + + _logger.info("Create prep producer to Q1"); + prepProducer1 = prepSession.createProducer(queue1); + + _logger.info("Create prep connection start"); + prepCon.start(); + + _logger.info("Create test connection"); + testCon = new AMQConnection("vm://:1", "guest", "guest", "TestConnection", "test"); + _logger.info("Create test session"); + testSession = testCon.createSession(false, AMQSession.NO_ACKNOWLEDGE); + _logger.info("Create test consumer of q2"); + testConsumer2 = testSession.createConsumer(queue2); + } + + protected void tearDown() throws Exception + { + _logger.info("Close connection"); + con.close(); + _logger.info("Close test connection"); + testCon.close(); + _logger.info("Close prep connection"); + prepCon.close(); + _logger.info("Kill broker"); + TransportConnection.killAllVMBrokers(); + super.tearDown(); + } + + public void testCommit() throws Exception + { + // add some messages + _logger.info("Send prep A"); + prepProducer1.send(prepSession.createTextMessage("A")); + _logger.info("Send prep B"); + prepProducer1.send(prepSession.createTextMessage("B")); + _logger.info("Send prep C"); + prepProducer1.send(prepSession.createTextMessage("C")); + + // send and receive some messages + _logger.info("Send X to Q2"); + producer2.send(session.createTextMessage("X")); + _logger.info("Send Y to Q2"); + producer2.send(session.createTextMessage("Y")); + _logger.info("Send Z to Q2"); + producer2.send(session.createTextMessage("Z")); + + _logger.info("Read A from Q1"); + expect("A", consumer1.receive(1000)); + _logger.info("Read B from Q1"); + expect("B", consumer1.receive(1000)); + _logger.info("Read C from Q1"); + expect("C", consumer1.receive(1000)); + + // commit + _logger.info("session commit"); + session.commit(); + _logger.info("Start test Connection"); + testCon.start(); + + // ensure sent messages can be received and received messages are gone + _logger.info("Read X from Q2"); + expect("X", testConsumer2.receive(1000)); + _logger.info("Read Y from Q2"); + expect("Y", testConsumer2.receive(1000)); + _logger.info("Read Z from Q2"); + expect("Z", testConsumer2.receive(1000)); + + _logger.info("create test session on Q1"); + testConsumer1 = testSession.createConsumer(queue1); + _logger.info("Read null from Q1"); + assertTrue(null == testConsumer1.receive(1000)); + _logger.info("Read null from Q2"); + assertTrue(null == testConsumer2.receive(1000)); + } + + public void testRollback() throws Exception + { + // add some messages + _logger.info("Send prep RB_A"); + prepProducer1.send(prepSession.createTextMessage("RB_A")); + _logger.info("Send prep RB_B"); + prepProducer1.send(prepSession.createTextMessage("RB_B")); + _logger.info("Send prep RB_C"); + prepProducer1.send(prepSession.createTextMessage("RB_C")); + + _logger.info("Sending RB_X RB_Y RB_Z"); + producer2.send(session.createTextMessage("RB_X")); + producer2.send(session.createTextMessage("RB_Y")); + producer2.send(session.createTextMessage("RB_Z")); + _logger.info("Receiving RB_A RB_B"); + expect("RB_A", consumer1.receive(1000)); + expect("RB_B", consumer1.receive(1000)); + // Don't consume 'RB_C' leave it in the prefetch cache to ensure rollback removes it. + // Quick sleep to ensure 'RB_C' gets pre-fetched + Thread.sleep(500); + + // rollback + _logger.info("rollback"); + session.rollback(); + + _logger.info("Receiving RB_A RB_B RB_C"); + // ensure sent messages are not visible and received messages are requeued + expect("RB_A", consumer1.receive(1000), true); + expect("RB_B", consumer1.receive(1000), true); + expect("RB_C", consumer1.receive(1000), true); + + _logger.info("Starting new connection"); + testCon.start(); + testConsumer1 = testSession.createConsumer(queue1); + _logger.info("Testing we have no messages left"); + assertTrue(null == testConsumer1.receive(1000)); + assertTrue(null == testConsumer2.receive(1000)); + + session.commit(); + + _logger.info("Testing we have no messages left after commit"); + assertTrue(null == testConsumer1.receive(1000)); + assertTrue(null == testConsumer2.receive(1000)); + } + + public void testResendsMsgsAfterSessionClose() throws Exception + { + AMQConnection con = new AMQConnection("vm://:1", "guest", "guest", "consumer1", "test"); + + Session consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); + AMQQueue queue3 = new AMQQueue(consumerSession.getDefaultQueueExchangeName(), new AMQShortString("Q3"), false); + MessageConsumer consumer = consumerSession.createConsumer(queue3); + + AMQConnection con2 = new AMQConnection("vm://:1", "guest", "guest", "producer1", "test"); + Session producerSession = con2.createSession(true, Session.SESSION_TRANSACTED); + MessageProducer producer = producerSession.createProducer(queue3); + + _logger.info("Sending four messages"); + producer.send(producerSession.createTextMessage("msg1")); + producer.send(producerSession.createTextMessage("msg2")); + producer.send(producerSession.createTextMessage("msg3")); + producer.send(producerSession.createTextMessage("msg4")); + + producerSession.commit(); + + _logger.info("Starting connection"); + con.start(); + TextMessage tm = (TextMessage) consumer.receive(); + assertNotNull(tm); + assertEquals("msg1", tm.getText()); + + consumerSession.commit(); + + _logger.info("Received and committed first message"); + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg2", tm.getText()); + + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg3", tm.getText()); + + tm = (TextMessage) consumer.receive(1000); + assertNotNull(tm); + assertEquals("msg4", tm.getText()); + + _logger.info("Received all four messages. Closing connection with three outstanding messages"); + + consumerSession.close(); + + consumerSession = con.createSession(true, Session.SESSION_TRANSACTED); + + consumer = consumerSession.createConsumer(queue3); + + // no ack for last three messages so when I call recover I expect to get three messages back + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg2", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg3", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + + tm = (TextMessage) consumer.receive(3000); + assertNotNull(tm); + assertEquals("msg4", tm.getText()); + assertTrue("Message is not redelivered", tm.getJMSRedelivered()); + + _logger.info("Received redelivery of three messages. Committing"); + + consumerSession.commit(); + + _logger.info("Called commit"); + + tm = (TextMessage) consumer.receive(1000); + assertNull(tm); + + _logger.info("No messages redelivered as is expected"); + + con.close(); + con2.close(); + } + + private void expect(String text, Message msg) throws JMSException + { + expect(text, msg, false); + } + + private void expect(String text, Message msg, boolean requeued) throws JMSException + { + assertNotNull("Message should not be null", msg); + assertTrue("Message should be a text message", msg instanceof TextMessage); + assertEquals("Message content does not match expected", text, ((TextMessage) msg).getText()); + assertEquals("Message should " + (requeued ? "" : "not") + " be requeued", requeued, msg.getJMSRedelivered()); + } + + public static junit.framework.Test suite() + { + return new junit.framework.TestSuite(TransactedTest.class); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/testutil/Config.java b/Final2/java/client/src/test/java/org/apache/qpid/testutil/Config.java new file mode 100644 index 0000000000..b777cf93b6 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/testutil/Config.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.testutil; + +import javax.jms.Connection; +import javax.jms.Destination; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQHeadersExchange; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; + +public class Config +{ + public static final String QUEUE = "queue"; + public static final String TOPIC = "topic"; + public static final String HEADERS = "headers"; + + private String host = "localhost"; + private int port = 5672; + private String type; + private String name = "simple_test_queue"; + + public Config() + { + this("localhost", 5672, QUEUE, "simple_test_queue"); + } + + public Config(String host, int port, String type, String name) + { + setHost(host); + setPort(port); + setType(type); + setName(name); + } + + 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 getType() + { + return type; + } + + public void setType(String type) + { + this.type = type; + } + + public boolean isQueue() + { + return QUEUE.equalsIgnoreCase(type); + } + + public boolean isTopic() + { + return TOPIC.equalsIgnoreCase(type); + } + + private boolean isHeaders() + { + return HEADERS.equalsIgnoreCase(type); + } + + public void setQueue(boolean queue) + { + type = queue ? QUEUE : TOPIC; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Destination getDestination() + { + if(isQueue()) + { + System.out.println("Using queue named " + name); + return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME,name); + } + else if(isTopic()) + { + System.out.println("Using topic named " + name); + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME,name); + } + else if(isHeaders()) + { + System.out.println("Using headers exhange named " + name); + return new AMQHeadersExchange(name); + } + return null; + } + + public Connection getConnection() throws Exception + { + System.out.println("Connecting to " + host + " on " + port + "..."); + return new AMQConnection(host, port, "guest", "guest", "Client" + System.currentTimeMillis(), "/test"); + } + + 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; + } + + private 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, e); + } + } + else if("-name".equalsIgnoreCase(key)) + { + setName(value); + } + else if("-type".equalsIgnoreCase(key)) + { + if(QUEUE.equalsIgnoreCase(value) + || TOPIC.equalsIgnoreCase(value) + || HEADERS.equalsIgnoreCase(value)) + { + type = value; + } + else{ + throw new RuntimeException("Bad destination type: " + value); + } + } + else + { + System.out.println("Ignoring unrecognised option: " + key); + } + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java b/Final2/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.java new file mode 100644 index 0000000000..7eb2abe7eb --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/testutil/QpidClientConnection.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.testutil; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionFactory; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.JMSAMQException; +import org.apache.qpid.url.URLSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; + +public class QpidClientConnection implements ExceptionListener +{ + private static final Logger _logger = LoggerFactory.getLogger(QpidClientConnection.class); + + private boolean transacted = true; + private int ackMode = Session.CLIENT_ACKNOWLEDGE; + private Connection connection; + + private String virtualHost; + private String brokerlist; + private int prefetch; + protected Session session; + protected boolean connected; + + public QpidClientConnection(String broker) + { + super(); + setVirtualHost("/test"); + setBrokerList(broker); + setPrefetch(5000); + } + + public void connect() throws JMSException + { + if (!connected) + { + /* + * amqp://[user:pass@][clientid]/virtualhost? + * brokerlist='[transport://]host[:port][?option='value'[&option='value']];' + * [&failover='method[?option='value'[&option='value']]'] + * [&option='value']" + */ + String brokerUrl = "amqp://guest:guest@" + virtualHost + "?brokerlist='" + brokerlist + "'"; + try + { + AMQConnectionFactory factory = new AMQConnectionFactory(new AMQConnectionURL(brokerUrl)); + _logger.info("connecting to Qpid :" + brokerUrl); + connection = factory.createConnection(); + + // register exception listener + connection.setExceptionListener(this); + + session = ((AMQConnection) connection).createSession(transacted, ackMode, prefetch); + + _logger.info("starting connection"); + connection.start(); + + connected = true; + } + catch (URLSyntaxException e) + { + throw new JMSAMQException("URL syntax error in [" + brokerUrl + "]: " + e.getMessage(), e); + } + } + } + + public void disconnect() throws JMSException + { + if (connected) + { + session.commit(); + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected"); + } + } + + public void disconnectWithoutCommit() throws JMSException + { + if (connected) + { + session.close(); + connection.close(); + connected = false; + _logger.info("disconnected without commit"); + } + } + + public String getBrokerList() + { + return brokerlist; + } + + public void setBrokerList(String brokerlist) + { + this.brokerlist = brokerlist; + } + + public String getVirtualHost() + { + return virtualHost; + } + + public void setVirtualHost(String virtualHost) + { + this.virtualHost = virtualHost; + } + + public void setPrefetch(int prefetch) + { + this.prefetch = prefetch; + } + + /** override as necessary */ + public void onException(JMSException exception) + { + _logger.info("ExceptionListener event: error " + exception.getErrorCode() + ", message: " + exception.getMessage()); + } + + public boolean isConnected() + { + return connected; + } + + public Session getSession() + { + return session; + } + + /** + * Put a String as a text messages, repeat n times. A null payload will result in a null message. + * + * @param queueName The queue name to put to + * @param payload the content of the payload + * @param copies the number of messages to put + * + * @throws javax.jms.JMSException any exception that occurs + */ + public void put(String queueName, String payload, int copies) throws JMSException + { + if (!connected) + { + connect(); + } + + _logger.info("putting to queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageProducer sender = session.createProducer(queue); + + for (int i = 0; i < copies; i++) + { + Message m = session.createTextMessage(payload + i); + m.setIntProperty("index", i + 1); + sender.send(m); + } + + session.commit(); + sender.close(); + _logger.info("put " + copies + " copies"); + } + + /** + * GET the top message on a queue. Consumes the message. Accepts timeout value. + * + * @param queueName The quename to get from + * @param readTimeout The timeout to use + * + * @return the content of the text message if any + * + * @throws javax.jms.JMSException any exception that occured + */ + public Message getNextMessage(String queueName, long readTimeout) throws JMSException + { + if (!connected) + { + connect(); + } + + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + + Message message = consumer.receive(readTimeout); + session.commit(); + consumer.close(); + + Message result; + + // all messages we consume should be TextMessages + if (message instanceof TextMessage) + { + result = ((TextMessage) message); + } + else if (null == message) + { + result = null; + } + else + { + _logger.info("warning: received non-text message"); + result = message; + } + + return result; + } + + /** + * GET the top message on a queue. Consumes the message. + * + * @param queueName The Queuename to get from + * + * @return The string content of the text message, if any received + * + * @throws javax.jms.JMSException any exception that occurs + */ + public Message getNextMessage(String queueName) throws JMSException + { + return getNextMessage(queueName, 0); + } + + /** + * Completely clears a queue. For readTimeout behaviour see Javadocs for javax.jms.MessageConsumer. + * + * @param queueName The Queue name to consume from + * @param readTimeout The timeout for each consume + * + * @throws javax.jms.JMSException Any exception that occurs during the consume + * @throws InterruptedException If the consume thread was interrupted during a consume. + */ + public void consume(String queueName, int readTimeout) throws JMSException, InterruptedException + { + if (!connected) + { + connect(); + } + + _logger.info("consuming queue " + queueName); + Queue queue = session.createQueue(queueName); + + final MessageConsumer consumer = session.createConsumer(queue); + int messagesReceived = 0; + + _logger.info("consuming..."); + while ((consumer.receive(readTimeout)) != null) + { + messagesReceived++; + } + + session.commit(); + consumer.close(); + _logger.info("consumed: " + messagesReceived); + } +} diff --git a/Final2/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java b/Final2/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.java new file mode 100644 index 0000000000..cedf1ac824 --- /dev/null +++ b/Final2/java/client/src/test/java/org/apache/qpid/testutil/VMBrokerSetup.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.testutil; + +import junit.extensions.TestSetup; +import junit.framework.Test; + +import org.apache.qpid.client.transport.TransportConnection; + +public class VMBrokerSetup extends TestSetup +{ + public VMBrokerSetup(Test t) + { + super(t); + } + + protected void setUp() throws Exception + { + super.setUp(); + try + { + TransportConnection.createVMBroker(1); + } + catch (Exception e) + { + fail("Unable to create broker: " + e); + } + } + + protected void tearDown() throws Exception + { + TransportConnection.killVMBroker(1); + super.tearDown(); + } +} diff --git a/Final2/java/client/test/bin/IBM-JNDI-Setup.bat b/Final2/java/client/test/bin/IBM-JNDI-Setup.bat new file mode 100644 index 0000000000..eb6a87fa9e --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-JNDI-Setup.sh b/Final2/java/client/test/bin/IBM-JNDI-Setup.sh new file mode 100755 index 0000000000..e3112f812d --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Publisher.bat b/Final2/java/client/test/bin/IBM-Publisher.bat new file mode 100644 index 0000000000..5bb4343c4c --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Publisher.sh b/Final2/java/client/test/bin/IBM-Publisher.sh new file mode 100755 index 0000000000..adecf040bc --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-PutGet.bat b/Final2/java/client/test/bin/IBM-PutGet.bat new file mode 100644 index 0000000000..c4316f1256 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-PutGet.sh b/Final2/java/client/test/bin/IBM-PutGet.sh new file mode 100755 index 0000000000..c75667c9f6 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-README.txt b/Final2/java/client/test/bin/IBM-README.txt new file mode 100644 index 0000000000..b076f3b3ca --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Receiver.bat b/Final2/java/client/test/bin/IBM-Receiver.bat new file mode 100644 index 0000000000..dff44d472a --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Receiver.sh b/Final2/java/client/test/bin/IBM-Receiver.sh new file mode 100755 index 0000000000..f50f0f744e --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Sender.bat b/Final2/java/client/test/bin/IBM-Sender.bat new file mode 100644 index 0000000000..b8826322e5 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Sender.sh b/Final2/java/client/test/bin/IBM-Sender.sh new file mode 100755 index 0000000000..b99429fd54 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Subscriber.bat b/Final2/java/client/test/bin/IBM-Subscriber.bat new file mode 100644 index 0000000000..5245639eba --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/IBM-Subscriber.sh b/Final2/java/client/test/bin/IBM-Subscriber.sh new file mode 100755 index 0000000000..43550100be --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/headersListener.sh b/Final2/java/client/test/bin/headersListener.sh new file mode 100755 index 0000000000..81930b7043 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/headersListenerGroup.sh b/Final2/java/client/test/bin/headersListenerGroup.sh new file mode 100755 index 0000000000..e1cc05cfd2 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/headersPublisher.sh b/Final2/java/client/test/bin/headersPublisher.sh new file mode 100755 index 0000000000..fd9fd26416 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/run_many.sh b/Final2/java/client/test/bin/run_many.sh new file mode 100755 index 0000000000..cca2ffec21 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/serviceProvidingClient.sh b/Final2/java/client/test/bin/serviceProvidingClient.sh new file mode 100755 index 0000000000..cbcf5a0f4b --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/serviceRequestingClient.sh b/Final2/java/client/test/bin/serviceRequestingClient.sh new file mode 100755 index 0000000000..213f44c00b --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/testService.sh b/Final2/java/client/test/bin/testService.sh new file mode 100755 index 0000000000..20161c3abf --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/topicListener.sh b/Final2/java/client/test/bin/topicListener.sh new file mode 100755 index 0000000000..ac0cb63c91 --- /dev/null +++ b/Final2/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/Final2/java/client/test/bin/topicPublisher.sh b/Final2/java/client/test/bin/topicPublisher.sh new file mode 100755 index 0000000000..e35c131fe8 --- /dev/null +++ b/Final2/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/Final2/java/client/test/etc/ApacheDS.properties b/Final2/java/client/test/etc/ApacheDS.properties new file mode 100644 index 0000000000..6c5cb4cec4 --- /dev/null +++ b/Final2/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/Final2/java/client/test/example_build.xml b/Final2/java/client/test/example_build.xml new file mode 100644 index 0000000000..a12862be04 --- /dev/null +++ b/Final2/java/client/test/example_build.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Final2/java/cluster/doc/design.doc b/Final2/java/cluster/doc/design.doc new file mode 100644 index 0000000000..c5bbf0f8a4 Binary files /dev/null and b/Final2/java/cluster/doc/design.doc differ diff --git a/Final2/java/cluster/pom.xml b/Final2/java/cluster/pom.xml new file mode 100644 index 0000000000..aaaf7d2f26 --- /dev/null +++ b/Final2/java/cluster/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + org.apache.qpid + qpid-cluster + jar + 1.0-incubating-M2 + Qpid Cluster + http://cwiki.apache.org/confluence/display/qpid + + + org.apache.qpid + qpid + 1.0-incubating-M2 + + + + .. + + + + + org.apache.qpid + qpid-common + + + org.apache.qpid + qpid-client + + + org.apache.qpid + qpid-broker + + + junit + junit + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.java new file mode 100644 index 0000000000..2baaa344ef --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQConnectionWaitException.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.cluster; + +import org.apache.qpid.AMQException; + +/** + * AMQConnectionWaitException represents a failure to connect to a cluster peer in a timely manner. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represents failure to connect to a cluster peer in a timely manner. + *
    + * + * @todo Not an AMQP exception as no status code. + */ +public class AMQConnectionWaitException extends AMQException +{ + public AMQConnectionWaitException(String s, Throwable e) + { + super(s, e); + + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.java new file mode 100644 index 0000000000..951bd22df0 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedBodyTypeException.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQBody; + +/** + * AMQUnexpectedBodyTypeException represents a failure where a message body does not match its expected type. For example, + * and AMQP method should have a method body. + * + *

    + *
    CRC Card
    Responsibilities Collaborations + *
    Represents a failure where a message body does not match 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. Check that the framing layer will pick up the error first. + */ +public class AMQUnexpectedBodyTypeException extends AMQException +{ + public AMQUnexpectedBodyTypeException(Class expectedClass, AMQBody body) + { + super("Unexpected body type. Expected: " + expectedClass.getName() + "; got: " + body.getClass().getName()); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.java new file mode 100644 index 0000000000..4dd318f90d --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/AMQUnexpectedFrameTypeException.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.cluster; + +import org.apache.qpid.AMQException; + +/** + * AMQUnexpectedFrameTypeException 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 AMQUnexpectedFrameTypeException extends AMQException +{ + public AMQUnexpectedFrameTypeException(String s) + { + super(s); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java new file mode 100644 index 0000000000..39508df566 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BlockingHandler.java @@ -0,0 +1,91 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.qpid.framing.AMQMethodBody; + +public class BlockingHandler implements ResponseHandler +{ + private final Class _expected; + private boolean _completed; + private AMQMethodBody _response; + + + public BlockingHandler() + { + this(AMQMethodBody.class); + } + + public BlockingHandler(Class expected) + { + _expected = expected; + } + + public void responded(AMQMethodBody response) + { + if (_expected.isInstance(response)) + { + _response = response; + completed(); + } + } + + public void removed() + { + completed(); + } + + private synchronized void completed() + { + _completed = true; + notifyAll(); + } + + synchronized void waitForCompletion() + { + while (!_completed) + { + try + { + wait(); + } + catch (InterruptedException ignore) + { + + } + } + } + + AMQMethodBody getResponse() + { + return _response; + } + + boolean failed() + { + return _response == null; + } + + boolean isCompleted() + { + return _completed; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.java new file mode 100644 index 0000000000..145aa58574 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BroadcastPolicy.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.cluster; + +public interface BroadcastPolicy +{ + public boolean isComplete(int responded, int members); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.java new file mode 100644 index 0000000000..7e2cf6da83 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Broker.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.log4j.Logger; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * An implementation of the Member interface (through which data is sent to other + * peers in the cluster). This class provides a base from which subclasses can + * inherit some common behaviour for broadcasting GroupRequests and sending methods + * that may expect a response. It also extends the Member abstraction to support + * a richer set of operations that are useful within the package but should not be + * exposed outside of it. + * + */ +abstract class Broker extends SimpleMemberHandle implements Member +{ + private static final Logger _logger = Logger.getLogger(Broker.class); + private static final int DEFAULT_CHANNEL = 1; + private static final int START_CHANNEL = 2; + private static final int END_CHANNEL = 10000; + + + private MemberFailureListener _listener; + //a wrap-around counter to allocate _requests a unique channel: + private int _nextChannel = START_CHANNEL; + //outstanding _requests: + private final Map _requests = new HashMap(); + + Broker(String host, int port) + { + super(host, port); + } + + /** + * Allows a listener to be registered that will receive callbacks when communication + * to the peer this broker instance represents fails. + * @param listener the callback to be notified of failures + */ + public void addFailureListener(MemberFailureListener listener) + { + _listener = listener; + } + + /** + * Allows subclasses to signal comunication failures + */ + protected void failed() + { + if (_listener != null) + { + _listener.failed(this); + } + } + + /** + * Subclasses should call this on receiving message responses from the remote + * peer. They are matched to any outstanding request they might be response + * to, with the completion and callback of that request being managed if + * required. + * + * @param channel the channel on which the method was received + * @param response the response received + * @return true if the response matched an outstanding request + */ + protected synchronized boolean handleResponse(int channel, AMQMethodBody response) + { + ResponseHandler request = _requests.get(channel); + if (request == null) + { + if(!_requests.isEmpty()) + { + _logger.warn(new LogMessage("[next channel={3, integer}]: Response {0} on channel {1, integer} failed to match outstanding requests: {2}", response, channel, _requests, _nextChannel)); + } + return false; + } + else + { + request.responded(response); + return true; + } + } + + /** + * Called when this broker is excluded from the group. Any requests made on + * it are informed this member has left the group. + */ + synchronized void remove() + { + for (ResponseHandler r : _requests.values()) + { + r.removed(); + } + } + + /** + * Engages this broker in the specified group request + * + * @param request the request being made to a group of brokers + * @throws AMQException if there is any failure + */ + synchronized void invoke(GroupRequest request) throws AMQException + { + int channel = nextChannel(); + _requests.put(channel, new GroupRequestAdapter(request, channel)); + request.send(channel, this); + } + + /** + * Sends a message to the remote peer and undertakes to notify the specified + * handler of the response. + * + * @param msg the message to send + * @param handler the callback to notify of responses (or the removal of this broker + * from the group) + * @throws AMQException + */ + synchronized void send(Sendable msg, ResponseHandler handler) throws AMQException + { + int channel; + if (handler != null) + { + channel = nextChannel(); + _requests.put(channel, new RemovingWrapper(handler, channel)); + } + else + { + channel = DEFAULT_CHANNEL; + } + + msg.send(channel, this); + } + + private int nextChannel() + { + int channel = _nextChannel++; + if(_nextChannel >= END_CHANNEL) + { + _nextChannel = START_CHANNEL; + } + return channel; + } + + /** + * extablish connection without handling redirect + */ + abstract boolean connect() throws IOException, InterruptedException; + + /** + * Start connection process, including replay + */ + abstract void connectAsynch(Iterable msgs); + + /** + * Replay messages to the remote peer this instance represents. These messages + * must be sent before any others whose transmission is requested through send() etc. + * + * @param msgs + */ + abstract void replay(Iterable msgs); + + /** + * establish connection, handling redirect if required... + */ + abstract Broker connectToCluster() throws IOException, InterruptedException; + + private class GroupRequestAdapter implements ResponseHandler + { + private final GroupRequest request; + private final int channel; + + GroupRequestAdapter(GroupRequest request, int channel) + { + this.request = request; + this.channel = channel; + } + + public void responded(AMQMethodBody response) + { + request.responseReceived(Broker.this, response); + _requests.remove(channel); + } + + public void removed() + { + request.removed(Broker.this); + } + + public String toString() + { + return "GroupRequestAdapter{" + channel + ", " + request + "}"; + } + } + + private class RemovingWrapper implements ResponseHandler + { + private final ResponseHandler handler; + private final int channel; + + RemovingWrapper(ResponseHandler handler, int channel) + { + this.handler = handler; + this.channel = channel; + } + + public void responded(AMQMethodBody response) + { + handler.responded(response); + _requests.remove(channel); + } + + public void removed() + { + handler.removed(); + } + + public String toString() + { + return "RemovingWrapper{" + channel + ", " + handler + "}"; + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.java new file mode 100644 index 0000000000..92c3c4e7bf --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerFactory.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.cluster; + +interface BrokerFactory +{ + public Broker create(MemberHandle handle); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java new file mode 100644 index 0000000000..755a341607 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/BrokerGroup.java @@ -0,0 +1,368 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.cluster.replay.ReplayManager; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.cluster.util.InvokeMultiple; +import org.apache.qpid.framing.AMQMethodBody; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Manages the membership list of a group and the set of brokers representing the + * remote peers. The group should be initialised through a call to establish() + * or connectToLeader(). + * + */ +class BrokerGroup +{ + private static final Logger _logger = Logger.getLogger(BrokerGroup.class); + + private final InvokeMultiple _changeListeners = new InvokeMultiple(MembershipChangeListener.class); + private final ReplayManager _replayMgr; + private final MemberHandle _local; + private final BrokerFactory _factory; + private final Object _lock = new Object(); + private final Set _synch = new HashSet(); + private List _members; + private List _peers = new ArrayList(); + private JoinState _state = JoinState.UNINITIALISED; + + /** + * Creates an unitialised group. + * + * @param local a handle that represents the local broker + * @param replayMgr the replay manager to use when creating new brokers + * @param factory the factory through which broker instances are created + */ + BrokerGroup(MemberHandle local, ReplayManager replayMgr, BrokerFactory factory) + { + _replayMgr = replayMgr; + _local = local; + _factory = factory; + } + + /** + * Called to establish the local broker as the leader of a new group + */ + void establish() + { + synchronized (_lock) + { + setState(JoinState.JOINED); + _members = new ArrayList(); + _members.add(_local); + } + fireChange(); + } + + /** + * Called by prospect to connect to group + */ + Broker connectToLeader(MemberHandle handle) throws Exception + { + Broker leader = _factory.create(handle); + leader = leader.connectToCluster(); + synchronized (_lock) + { + setState(JoinState.JOINING); + _members = new ArrayList(); + _members.add(leader); + _peers.add(leader); + } + fireChange(); + return leader; + } + + /** + * Called by leader when handling a join request + */ + Broker connectToProspect(MemberHandle handle) throws IOException, InterruptedException + { + Broker prospect = _factory.create(handle); + prospect.connect(); + synchronized (_lock) + { + _members.add(prospect); + _peers.add(prospect); + } + fireChange(); + return prospect; + } + + /** + * Called in reponse to membership announcements. + * + * @param members the list of members now part of the group + */ + void setMembers(List members) + { + if (isJoined()) + { + List old = _peers; + + synchronized (_lock) + { + _peers = getBrokers(members); + _members = new ArrayList(members); + } + + //remove those that are still members + old.removeAll(_peers); + + //handle failure of any brokers that haven't survived + for (Broker peer : old) + { + peer.remove(); + } + } + else + { + synchronized (_lock) + { + setState(JoinState.INITIATION); + _members = new ArrayList(members); + _synch.addAll(_members); + _synch.remove(_local); + } + } + fireChange(); + } + + List getMembers() + { + synchronized (_lock) + { + return Collections.unmodifiableList(_members); + } + } + + List getPeers() + { + synchronized (_lock) + { + return _peers; + } + } + + /** + * Removes the member presented from the group + * @param peer the broker that should be removed + */ + void remove(Broker peer) + { + synchronized (_lock) + { + _peers.remove(peer); + _members.remove(peer); + } + fireChange(); + } + + MemberHandle getLocal() + { + return _local; + } + + Broker getLeader() + { + synchronized (_lock) + { + return _peers.size() > 0 ? _peers.get(0) : null; + } + } + + /** + * Allows a Broker instance to be retrieved for a given handle + * + * @param handle the handle for which a broker is sought + * @param create flag to indicate whther a broker should be created for the handle if + * one is not found within the list of known peers + * @return the broker corresponding to handle or null if a match cannot be found and + * create is false + */ + Broker findBroker(MemberHandle handle, boolean create) + { + if (handle instanceof Broker) + { + return (Broker) handle; + } + else + { + for (Broker b : getPeers()) + { + if (b.matches(handle)) + { + return b; + } + } + } + if (create) + { + Broker b = _factory.create(handle); + List msgs = _replayMgr.replay(isLeader(_local)); + _logger.info(new LogMessage("Replaying {0} from {1} to {2}", msgs, _local, b)); + b.connectAsynch(msgs); + + return b; + } + else + { + return null; + } + } + + /** + * @param member the member to test for leadership + * @return true if the passed in member is the group leader, false otherwise + */ + boolean isLeader(MemberHandle member) + { + synchronized (_lock) + { + return member.matches(_members.get(0)); + } + } + + /** + * @return true if the local broker is the group leader, false otherwise + */ + boolean isLeader() + { + return isLeader(_local); + } + + /** + * Used when the leader fails and the next broker in the list needs to + * assume leadership + * @return true if the action succeeds + */ + boolean assumeLeadership() + { + boolean valid; + synchronized (_lock) + { + valid = _members.size() > 1 && _local.matches(_members.get(1)); + if (valid) + { + _members.remove(0); + _peers.remove(0); + } + } + fireChange(); + return valid; + } + + /** + * Called in response to a Cluster.Synch message being received during the join + * process. This indicates that the member mentioned has replayed all necessary + * messages to the local broker. + * + * @param member the member from whom the synch messages was received + */ + void synched(MemberHandle member) + { + _logger.info(new LogMessage("Synchronised with {0}", member)); + synchronized (_lock) + { + if (isLeader(member)) + { + setState(JoinState.INDUCTION); + } + _synch.remove(member); + if (_synch.isEmpty()) + { + _peers = getBrokers(_members); + setState(JoinState.JOINED); + } + } + } + + + /** + * @return the state of the group + */ + JoinState getState() + { + synchronized (_lock) + { + return _state; + } + } + + void addMemberhipChangeListener(MembershipChangeListener l) + { + _changeListeners.addListener(l); + } + + void removeMemberhipChangeListener(MembershipChangeListener l) + { + _changeListeners.removeListener(l); + } + + + + private void setState(JoinState state) + { + _logger.info(new LogMessage("Changed state from {0} to {1}", _state, state)); + _state = state; + } + + private boolean isJoined() + { + return inState(JoinState.JOINED); + } + + private boolean inState(JoinState state) + { + return _state.equals(state); + } + + private List getBrokers(List handles) + { + List brokers = new ArrayList(); + for (MemberHandle handle : handles) + { + if (!_local.matches(handle)) + { + brokers.add(findBroker(handle, true)); + } + } + return brokers; + } + + private void fireChange() + { + List members; + synchronized(this) + { + members = new ArrayList(_members); + } + _changeListeners.getProxy().changed(Collections.unmodifiableList(members)); + } +} \ No newline at end of file diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.java new file mode 100644 index 0000000000..1b4a3e8327 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientAdapter.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.cluster; + +import org.apache.mina.common.IoSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.client.state.AMQStateManager; +import org.apache.qpid.framing.AMQMethodBody; + +/** + * Hack to assist with reuse of the client handlers for connection setup in + * the inter-broker communication within the cluster. + * + */ +class ClientAdapter implements MethodHandler +{ + private final AMQProtocolSession _session; + private final AMQStateManager _stateMgr; + + ClientAdapter(IoSession session, AMQStateManager stateMgr) + { + this(session, stateMgr, "guest", "guest", session.toString(), "/cluster"); + } + + ClientAdapter(IoSession session, AMQStateManager stateMgr, String user, String password, String name, String path) + { + _session = new SessionAdapter(session, new ConnectionAdapter(user, password, name, path)); + _stateMgr = stateMgr; + } + + public void handle(int channel, AMQMethodBody method) throws AMQException + { + AMQMethodEvent evt = new AMQMethodEvent(channel, method); + _stateMgr.methodReceived(evt); + } + + private class SessionAdapter extends AMQProtocolSession + { + public SessionAdapter(IoSession session, AMQConnection connection) + { + super(null, session, connection); + } + } + + private static class ConnectionAdapter extends AMQConnection + { + ConnectionAdapter(String username, String password, String clientName, String virtualPath) + { + super(username, password, clientName, virtualPath); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java new file mode 100644 index 0000000000..c1caf8bbff --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClientHandlerRegistry.java @@ -0,0 +1,130 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.qpid.client.handler.ConnectionCloseMethodHandler; +import org.apache.qpid.client.handler.ConnectionOpenOkMethodHandler; +import org.apache.qpid.client.handler.ConnectionSecureMethodHandler; +import org.apache.qpid.client.handler.ConnectionStartMethodHandler; +import org.apache.qpid.client.handler.ConnectionTuneMethodHandler; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.client.state.AMQStateManager; +// import org.apache.qpid.client.state.IllegalStateTransitionException; +import org.apache.qpid.client.state.StateAwareMethodListener; +import org.apache.qpid.client.protocol.AMQProtocolSession; +import org.apache.qpid.framing.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * An extension of client.AMQStateManager that allows different handlers to be registered. + * + */ +public class ClientHandlerRegistry extends AMQStateManager +{ + private final Map _handlers = new HashMap(); + private final MemberHandle _identity; + + protected ClientHandlerRegistry(MemberHandle local, AMQProtocolSession protocolSession) + { + super(AMQState.CONNECTION_NOT_STARTED, false, protocolSession); + + _identity = local; + + addHandler(ConnectionStartBody.class, ConnectionStartMethodHandler.getInstance(), + AMQState.CONNECTION_NOT_STARTED); + + addHandler(ConnectionTuneBody.class, new ConnectionTuneHandler(), + AMQState.CONNECTION_NOT_TUNED); + addHandler(ConnectionSecureBody.class, ConnectionSecureMethodHandler.getInstance(), + AMQState.CONNECTION_NOT_TUNED); + addHandler(ConnectionOpenOkBody.class, ConnectionOpenOkMethodHandler.getInstance(), + AMQState.CONNECTION_NOT_OPENED); + + addHandlers(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance(), + AMQState.CONNECTION_NOT_STARTED, + AMQState.CONNECTION_NOT_TUNED, + AMQState.CONNECTION_NOT_OPENED); + + } + + private ClientRegistry state(AMQState state) + { + ClientRegistry registry = _handlers.get(state); + if (registry == null) + { + registry = new ClientRegistry(); + _handlers.put(state, registry); + } + return registry; + } + + protected StateAwareMethodListener findStateTransitionHandler(AMQState state, AMQMethodBody frame) //throws IllegalStateTransitionException + { + ClientRegistry registry = _handlers.get(state); + return registry == null ? null : registry.getHandler(frame); + } + + + > void addHandlers(Class type, StateAwareMethodListener handler, AMQState... states) + { + for (AMQState state : states) + { + addHandler(type, handler, state); + } + } + + > void addHandler(Class type, StateAwareMethodListener handler, AMQState state) + { + ClientRegistry registry = _handlers.get(state); + if (registry == null) + { + registry = new ClientRegistry(); + _handlers.put(state, registry); + } + registry.add(type, handler); + } + + static class ClientRegistry + { + private final Map, StateAwareMethodListener> registry + = new HashMap, StateAwareMethodListener>(); + + > void add(A type, StateAwareMethodListener handler) + { + registry.put(type, handler); + } + + StateAwareMethodListener getHandler(AMQMethodBody frame) + { + return registry.get(frame.getClass()); + } + } + + class ConnectionTuneHandler extends ConnectionTuneMethodHandler + { + protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities, boolean insist, byte major, byte minor) + { + return super.createConnectionOpenFrame(channel, path, new AMQShortString(ClusterCapability.add(capabilities, _identity)), insist, major, minor); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.java new file mode 100644 index 0000000000..80f9ef62b1 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterBuilder.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.cluster; + +import org.apache.qpid.server.cluster.handler.ClusterMethodHandlerFactory; +import org.apache.qpid.server.cluster.replay.RecordingMethodHandlerFactory; +import org.apache.qpid.server.cluster.replay.ReplayStore; + +import java.net.InetSocketAddress; + +class ClusterBuilder +{ + private final LoadTable loadTable = new LoadTable(); + private final ReplayStore replayStore = new ReplayStore(); + private final MemberHandle handle; + private final GroupManager groupMgr; + + ClusterBuilder(InetSocketAddress address) + { + handle = new SimpleMemberHandle(address.getHostName(), address.getPort()).resolve(); + groupMgr = new DefaultGroupManager(handle, getBrokerFactory(), replayStore, loadTable); + } + + GroupManager getGroupManager() + { + return groupMgr; + } + + ServerHandlerRegistry getHandlerRegistry() + { + return new ServerHandlerRegistry(getHandlerFactory(), null, null); + } + + private MethodHandlerFactory getHandlerFactory() + { + MethodHandlerFactory factory = new ClusterMethodHandlerFactory(groupMgr, loadTable); + //need to wrap relevant handlers with recording handler for easy replay: + return new RecordingMethodHandlerFactory(factory, replayStore); + } + + private BrokerFactory getBrokerFactory() + { + return new MinaBrokerProxyFactory(handle); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.java new file mode 100644 index 0000000000..57c48f0611 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusterCapability.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.cluster; + +import org.apache.qpid.framing.AMQShortString; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClusterCapability +{ + public static final String PATTERN = ".*\\bcluster_peer=(\\S*:\\d*)\b*.*"; + public static final String PEER = "cluster_peer"; + + public static AMQShortString add(AMQShortString original, MemberHandle identity) + { + return original == null ? peer(identity) : new AMQShortString(original + " " + peer(identity)); + } + + private static AMQShortString peer(MemberHandle identity) + { + return new AMQShortString(PEER + "=" + identity.getDetails()); + } + + public static boolean contains(AMQShortString in) + { + return in != null; // && in.contains(in); + } + + public static MemberHandle getPeer(AMQShortString in) + { + Matcher matcher = Pattern.compile(PATTERN).matcher(in); + if (matcher.matches()) + { + return new SimpleMemberHandle(matcher.group(1)); + } + else + { + throw new RuntimeException("Could not find peer in '" + in + "'"); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java new file mode 100644 index 0000000000..ee5aa48db9 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolHandler.java @@ -0,0 +1,190 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.log4j.Logger; +import org.apache.mina.common.IoSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.ConnectionOpenBody; +import org.apache.qpid.framing.ConnectionSecureOkBody; +import org.apache.qpid.framing.ConnectionStartOkBody; +import org.apache.qpid.framing.ConnectionTuneOkBody; +import org.apache.qpid.framing.ClusterMembershipBody; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQPFastProtocolHandler; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +import java.net.InetSocketAddress; + +public class ClusteredProtocolHandler extends AMQPFastProtocolHandler implements InductionBuffer.MessageHandler +{ + private static final Logger _logger = Logger.getLogger(ClusteredProtocolHandler.class); + private final InductionBuffer _peerBuffer = new InductionBuffer(this); + private final InductionBuffer _clientBuffer = new InductionBuffer(this); + private final GroupManager _groupMgr; + private final ServerHandlerRegistry _handlers; + + public ClusteredProtocolHandler(InetSocketAddress address) + { + this(ApplicationRegistry.getInstance(), address); + } + + public ClusteredProtocolHandler(IApplicationRegistry registry, InetSocketAddress address) + { + super(registry); + ClusterBuilder builder = new ClusterBuilder(address); + _groupMgr = builder.getGroupManager(); + _handlers = builder.getHandlerRegistry(); + } + + public ClusteredProtocolHandler(ClusteredProtocolHandler handler) + { + super(handler); + _groupMgr = handler._groupMgr; + _handlers = handler._handlers; + } + + protected void createSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession, AMQCodecFactory codec) throws AMQException + { + new ClusteredProtocolSession(session, virtualHostRegistry, codec, new ServerHandlerRegistry(_handlers, virtualHostRegistry, protocolSession)); + } + + void connect(String join) throws Exception + { + if (join == null) + { + _groupMgr.establish(); + } + else + { + _groupMgr.join(new SimpleMemberHandle(join)); + } + } + + private boolean inState(JoinState state) + { + return _groupMgr.getState().equals(state); + } + + public void messageReceived(IoSession session, Object msg) throws Exception + { + JoinState state = _groupMgr.getState(); + switch (state) + { + case JOINED: + _logger.debug(new LogMessage("Received {0}", msg)); + super.messageReceived(session, msg); + break; + case JOINING: + case INITIATION: + case INDUCTION: + buffer(session, msg); + break; + default: + throw new AMQException("Received message while in state: " + state); + } + JoinState latest = _groupMgr.getState(); + if (!latest.equals(state)) + { + switch (latest) + { + case INDUCTION: + _logger.info("Reached induction, delivering buffered message from peers"); + _peerBuffer.deliver(); + break; + case JOINED: + _logger.info("Reached joined, delivering buffered message from clients"); + _clientBuffer.deliver(); + break; + } + } + } + + private void buffer(IoSession session, Object msg) throws Exception + { + if (isBufferable(msg)) + { + MemberHandle peer = ClusteredProtocolSession.getSessionPeer(session); + if (peer == null) + { + _logger.debug(new LogMessage("Buffering {0} for client", msg)); + _clientBuffer.receive(session, msg); + } + else if (inState(JoinState.JOINING) && isMembershipAnnouncement(msg)) + { + _logger.debug(new LogMessage("Initial membership [{0}] received from {1}", msg, peer)); + super.messageReceived(session, msg); + } + else if (inState(JoinState.INITIATION) && _groupMgr.isLeader(peer)) + { + _logger.debug(new LogMessage("Replaying {0} from leader ", msg)); + super.messageReceived(session, msg); + } + else if (inState(JoinState.INDUCTION)) + { + _logger.debug(new LogMessage("Replaying {0} from peer {1}", msg, peer)); + super.messageReceived(session, msg); + } + else + { + _logger.debug(new LogMessage("Buffering {0} for peer {1}", msg, peer)); + _peerBuffer.receive(session, msg); + } + } + else + { + _logger.debug(new LogMessage("Received {0}", msg)); + super.messageReceived(session, msg); + } + } + + public void deliver(IoSession session, Object msg) throws Exception + { + _logger.debug(new LogMessage("Delivering {0}", msg)); + super.messageReceived(session, msg); + } + + private boolean isMembershipAnnouncement(Object msg) + { + return msg instanceof AMQFrame && (((AMQFrame) msg).getBodyFrame() instanceof ClusterMembershipBody); + } + + private boolean isBufferable(Object msg) + { + return msg instanceof AMQFrame && isBuffereable(((AMQFrame) msg).getBodyFrame()); + } + + private boolean isBuffereable(AMQBody body) + { + return !(body instanceof ConnectionStartOkBody || + body instanceof ConnectionTuneOkBody || + body instanceof ConnectionSecureOkBody || + body instanceof ConnectionOpenBody); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.java new file mode 100644 index 0000000000..eea660c4f0 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ClusteredProtocolSession.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.server.cluster; + +import org.apache.mina.common.IoSession; +import org.apache.qpid.AMQException; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQMinaProtocolSession; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQMessage; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.state.AMQStateManager; + +public class ClusteredProtocolSession extends AMQMinaProtocolSession +{ + private MemberHandle _peer; + + public ClusteredProtocolSession(IoSession session, VirtualHostRegistry virtualHostRegistry, AMQCodecFactory codecFactory, AMQStateManager stateManager) throws AMQException +// public ClusteredProtocolSession(IoSession session, QueueRegistry queueRegistry, +// ExchangeRegistry exchangeRegistry, AMQCodecFactory codecFactory) throws AMQException + { + super(session, virtualHostRegistry, codecFactory, stateManager); +// super(session, queueRegistry, exchangeRegistry, codecFactory); + } + + public boolean isPeerSession() + { + return _peer != null; + } + + public void setSessionPeer(MemberHandle peer) + { + _peer = peer; + } + + public MemberHandle getSessionPeer() + { + return _peer; + } + + public AMQChannel getChannel(int channelId) + throws AMQException + { + AMQChannel channel = super.getChannel(channelId); + if (isPeerSession() && channel == null) + { + channel = new OneUseChannel(channelId, getVirtualHost()); + addChannel(channel); + } + return channel; + } + + public static boolean isPeerSession(IoSession session) + { + return isPeerSession(getAMQProtocolSession(session)); + } + + public static boolean isPeerSession(AMQProtocolSession session) + { + return session instanceof ClusteredProtocolSession && ((ClusteredProtocolSession) session).isPeerSession(); + } + + public static void setSessionPeer(AMQProtocolSession session, MemberHandle peer) + { + ((ClusteredProtocolSession) session).setSessionPeer(peer); + } + + public static MemberHandle getSessionPeer(AMQProtocolSession session) + { + return ((ClusteredProtocolSession) session).getSessionPeer(); + } + + public static MemberHandle getSessionPeer(IoSession session) + { + return getSessionPeer(getAMQProtocolSession(session)); + } + + /** + * Cleans itself up after delivery of a message (publish frame, header and optional body frame(s)) + */ + private class OneUseChannel extends AMQChannel + { + public OneUseChannel(int channelId, VirtualHost virtualHost) + throws AMQException + { + super(ClusteredProtocolSession.this,channelId, + virtualHost.getMessageStore(), + virtualHost.getExchangeRegistry()); + } + + protected void routeCurrentMessage() throws AMQException + { + super.routeCurrentMessage(); + removeChannel(getChannelId()); + } + } + + public static boolean isPayloadFromPeer(AMQMessage payload) + { + return isPeerSession(payload.getPublisher()); + } + + public static boolean canRelay(AMQMessage payload, MemberHandle target) + { + //can only relay client messages that have not already been relayed to the given target + return !isPayloadFromPeer(payload) && !payload.checkToken(target); + } + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java new file mode 100644 index 0000000000..a1f01eff46 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ConnectionStatusMonitor.java @@ -0,0 +1,80 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import java.io.IOException; + +class ConnectionStatusMonitor +{ + private boolean _complete; + private boolean _redirected; + private String _host; + private int _port; + private RuntimeException _error; + + synchronized void opened() + { + _complete = true; + notifyAll(); + } + + synchronized void redirect(String host, int port) + { + _complete = true; + _redirected = true; + this._host = host; + this._port = port; + } + + synchronized void failed(RuntimeException e) + { + _error = e; + _complete = true; + } + + synchronized boolean waitUntilOpen() throws InterruptedException + { + while (!_complete) + { + wait(); + } + if (_error != null) + { + throw _error; + } + return !_redirected; + } + + synchronized boolean isOpened() + { + return _complete; + } + + String getHost() + { + return _host; + } + + int getPort() + { + return _port; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java new file mode 100644 index 0000000000..2f473b63fb --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/DefaultGroupManager.java @@ -0,0 +1,396 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.cluster.policy.StandardPolicies; +import org.apache.qpid.server.cluster.replay.ReplayManager; +import org.apache.qpid.server.cluster.util.LogMessage; + +import java.util.List; + +public class DefaultGroupManager implements GroupManager, MemberFailureListener, BrokerFactory, StandardPolicies +{ + private static final Logger _logger = Logger.getLogger(DefaultGroupManager.class); + private final LoadTable _loadTable; + private final BrokerFactory _factory; + private final ReplayManager _replayMgr; + private final BrokerGroup _group; + + DefaultGroupManager(MemberHandle handle, BrokerFactory factory, ReplayManager replayMgr) + { + this(handle, factory, replayMgr, new LoadTable()); + } + + DefaultGroupManager(MemberHandle handle, BrokerFactory factory, ReplayManager replayMgr, LoadTable loadTable) + { + handle = SimpleMemberHandle.resolve(handle); + _logger.info(handle); + _loadTable = loadTable; + _factory = factory; + _replayMgr = replayMgr; + _group = new BrokerGroup(handle, _replayMgr, this); + } + + public JoinState getState() + { + return _group.getState(); + } + + public void addMemberhipChangeListener(MembershipChangeListener l) + { + _group.addMemberhipChangeListener(l); + } + + public void removeMemberhipChangeListener(MembershipChangeListener l) + { + _group.removeMemberhipChangeListener(l); + } + + public void broadcast(Sendable message) throws AMQException + { + for (Broker b : _group.getPeers()) + { + b.send(message, null); + } + } + + public void broadcast(Sendable message, BroadcastPolicy policy, GroupResponseHandler callback) throws AMQException + { + GroupRequest request = new GroupRequest(message, policy, callback); + for (Broker b : _group.getPeers()) + { + b.invoke(request); + } + request.finishedSend(); + } + + public void send(MemberHandle broker, Sendable message) throws AMQException + { + Broker destination = findBroker(broker); + if(destination == null) + { + _logger.warn(new LogMessage("Invalid destination sending {0}. {1} not known", message, broker)); + } + else + { + destination.send(message, null); + _logger.debug(new LogMessage("Sent {0} to {1}", message, broker)); + } + } + + private void send(Broker broker, Sendable message, ResponseHandler handler) throws AMQException + { + broker.send(message, handler); + } + + private void ping(Broker b) throws AMQException + { + // 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. + ClusterPingBody ping = new ClusterPingBody((byte)8, + (byte)0, + ClusterPingBody.getClazz((byte)8, (byte)0), + ClusterPingBody.getMethod((byte)8, (byte)0), + _group.getLocal().getDetails(), + _loadTable.getLocalLoad(), + true); + BlockingHandler handler = new BlockingHandler(); + send(getLeader(), new SimpleBodySendable(ping), handler); + handler.waitForCompletion(); + if (handler.failed()) + { + if (isLeader()) + { + handleFailure(b); + } + else + { + suspect(b); + } + } + else + { + _loadTable.setLoad(b, ((ClusterPingBody) handler.getResponse()).load); + } + } + + public void handlePing(MemberHandle member, long load) + { + _loadTable.setLoad(findBroker(member), load); + } + + public Member redirect() + { + return _loadTable.redirect(); + } + + public void establish() + { + _group.establish(); + _logger.info("Established cluster"); + } + + public void join(MemberHandle member) throws AMQException + { + member = SimpleMemberHandle.resolve(member); + + Broker leader = connectToLeader(member); + _logger.info(new LogMessage("Connected to {0}. joining", leader)); + // 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. + ClusterJoinBody join = new ClusterJoinBody((byte)8, + (byte)0, + ClusterJoinBody.getClazz((byte)8, (byte)0), + ClusterJoinBody.getMethod((byte)8, (byte)0), + _group.getLocal().getDetails()); + + send(leader, new SimpleBodySendable(join)); + } + + private Broker connectToLeader(MemberHandle member) throws AMQException + { + try + { + return _group.connectToLeader(member); + } + catch (Exception e) + { + throw new AMQException("Could not connect to leader: " + e, e); + } + } + + public void leave() throws AMQException + { + // 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. + ClusterLeaveBody leave = new ClusterLeaveBody((byte)8, + (byte)0, + ClusterLeaveBody.getClazz((byte)8, (byte)0), + ClusterLeaveBody.getMethod((byte)8, (byte)0), + _group.getLocal().getDetails()); + + send(getLeader(), new SimpleBodySendable(leave)); + } + + private void suspect(MemberHandle broker) throws AMQException + { + if (_group.isLeader(broker)) + { + //need new leader, if this broker is next in line it can assume leadership + if (_group.assumeLeadership()) + { + announceMembership(); + } + else + { + _logger.warn(new LogMessage("Leader failed. Expecting {0} to succeed.", _group.getMembers().get(1))); + } + } + else + { + // 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. + ClusterSuspectBody suspect = new ClusterSuspectBody((byte)8, + (byte)0, + ClusterSuspectBody.getClazz((byte)8, (byte)0), + ClusterSuspectBody.getMethod((byte)8, (byte)0), + broker.getDetails()); + + send(getLeader(), new SimpleBodySendable(suspect)); + } + } + + + public void handleJoin(MemberHandle member) throws AMQException + { + _logger.info(new LogMessage("Handling join request for {0}", member)); + if(isLeader()) + { + //connect to the host and port specified: + Broker prospect = connectToProspect(member); + announceMembership(); + List msgs = _replayMgr.replay(true); + _logger.info(new LogMessage("Replaying {0} from leader to {1}", msgs, prospect)); + prospect.replay(msgs); + } + else + { + //pass request on to leader: + // 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. + ClusterJoinBody request = new ClusterJoinBody((byte)8, (byte)0, + ClusterJoinBody.getClazz((byte)8, (byte)0), + ClusterJoinBody.getMethod((byte)8, (byte)0), + member.getDetails()); + + Broker leader = getLeader(); + send(leader, new SimpleBodySendable(request)); + _logger.info(new LogMessage("Passed join request for {0} to {1}", member, leader)); + } + } + + private Broker connectToProspect(MemberHandle member) throws AMQException + { + try + { + return _group.connectToProspect(member); + } + catch (Exception e) + { + e.printStackTrace(); + throw new AMQException("Could not connect to prospect: " + e, e); + } + } + + public void handleLeave(MemberHandle member) throws AMQException + { + handleFailure(findBroker(member)); + announceMembership(); + } + + public void handleSuspect(MemberHandle member) throws AMQException + { + Broker b = findBroker(member); + if(b != null) + { + //ping it to check it has failed, ping will handle failure if it has + ping(b); + announceMembership(); + } + } + + public void handleSynch(MemberHandle member) + { + _group.synched(member); + } + + private ClusterMembershipBody createAnnouncement(String membership) + { + // 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. + ClusterMembershipBody announce = new ClusterMembershipBody((byte)8, (byte)0, + ClusterMembershipBody.getClazz((byte)8, (byte)0), + ClusterMembershipBody.getMethod((byte)8, (byte)0), + membership.getBytes()); + + + return announce; + } + + private void announceMembership() throws AMQException + { + String membership = SimpleMemberHandle.membersToString(_group.getMembers()); + ClusterMembershipBody announce = createAnnouncement(membership); + broadcast(new SimpleBodySendable(announce)); + _logger.info(new LogMessage("Membership announcement sent: {0}", membership)); + } + + private void handleFailure(Broker peer) + { + peer.remove(); + _group.remove(peer); + } + + public void handleMembershipAnnouncement(String membership) throws AMQException + { + _group.setMembers(SimpleMemberHandle.stringToMembers(membership)); + _logger.info(new LogMessage("Membership announcement received: {0}", membership)); + } + + public boolean isLeader() + { + return _group.isLeader(); + } + + public boolean isLeader(MemberHandle handle) + { + return _group.isLeader(handle); + } + + public Broker getLeader() + { + return _group.getLeader(); + } + + private Broker findBroker(MemberHandle handle) + { + return _group.findBroker(handle, false); + } + + public Member getMember(MemberHandle handle) + { + return findBroker(handle); + } + + public boolean isMember(MemberHandle member) + { + for (MemberHandle handle : _group.getMembers()) + { + if (handle.matches(member)) + { + return true; + } + } + return false; + } + + public MemberHandle getLocal() + { + return _group.getLocal(); + } + + public void failed(MemberHandle member) + { + if (isLeader()) + { + handleFailure(findBroker(member)); + try + { + announceMembership(); + } + catch (AMQException e) + { + _logger.error("Error announcing failure: " + e, e); + } + } + else + { + try + { + suspect(member); + } + catch (AMQException e) + { + _logger.error("Error sending suspect: " + e, e); + } + } + } + + public Broker create(MemberHandle handle) + { + Broker broker = _factory.create(handle); + broker.addFailureListener(this); + return broker; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.java new file mode 100644 index 0000000000..5599ae4b1f --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupManager.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.cluster; + +import org.apache.qpid.AMQException; + +public interface GroupManager +{ + /** + * Establish a new cluster with the local member as the leader. + */ + public void establish(); + + /** + * Join the cluster to which member belongs + */ + public void join(MemberHandle member) throws AMQException; + + public void broadcast(Sendable message) throws AMQException; + + public void broadcast(Sendable message, BroadcastPolicy policy, GroupResponseHandler callback) throws AMQException; + + public void send(MemberHandle broker, Sendable message) throws AMQException; + + public void leave() throws AMQException; + + public void handleJoin(MemberHandle member) throws AMQException; + + public void handleLeave(MemberHandle member) throws AMQException; + + public void handleSuspect(MemberHandle member) throws AMQException; + + public void handlePing(MemberHandle member, long load); + + public void handleMembershipAnnouncement(String membership) throws AMQException; + + public void handleSynch(MemberHandle member); + + public boolean isLeader(); + + public boolean isLeader(MemberHandle handle); + + public boolean isMember(MemberHandle member); + + public MemberHandle redirect(); + + public MemberHandle getLocal(); + + public JoinState getState(); + + public void addMemberhipChangeListener(MembershipChangeListener l); + + public void removeMemberhipChangeListener(MembershipChangeListener l); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java new file mode 100644 index 0000000000..8ab7856e87 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupRequest.java @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Represents a method sent to a group of Member instances. Manages the responses, + * completion and callback. + * + */ +class GroupRequest +{ + private final Map _responses = new HashMap(); + private final List _brokers = new ArrayList(); + private boolean _sent; + + private final Sendable _request; + private final BroadcastPolicy _policy; + private final GroupResponseHandler _callback; + + GroupRequest(Sendable request, BroadcastPolicy policy, GroupResponseHandler callback) + { + _request = request; + _policy = policy; + _callback = callback; + } + + void send(int channel, Member session) throws AMQException + { + _brokers.add(session); + _request.send(channel, session); + } + + boolean finishedSend() + { + _sent = true; + return checkCompletion(); + } + + public boolean responseReceived(Member broker, AMQMethodBody response) + { + _responses.put(broker, response); + return checkCompletion(); + } + + public boolean removed(Member broker) + { + _brokers.remove(broker); + return checkCompletion(); + } + + private synchronized boolean checkCompletion() + { + return isComplete() && callback(); + } + + boolean isComplete() + { + return _sent && _policy != null && _policy.isComplete(_responses.size(), _brokers.size()); + } + + boolean callback() + { + _callback.response(getResults(), _brokers); + return true; + } + + List getResults() + { + List results = new ArrayList(_brokers.size()); + for (Member b : _brokers) + { + results.add(_responses.get(b)); + } + return results; + } + + public String toString() + { + return "GroupRequest{request=" + _request +", brokers=" + _brokers + ", responses=" + _responses + "}"; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.java new file mode 100644 index 0000000000..d2e9de2f39 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/GroupResponseHandler.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.server.cluster; + +import org.apache.qpid.framing.AMQMethodBody; + +import java.util.List; + +public interface GroupResponseHandler +{ + //Note: this implies that the response to a group request will always be a method body... + public void response(List responses, List members); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.java new file mode 100644 index 0000000000..586d7d4ae8 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/InductionBuffer.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.server.cluster; + +import org.apache.mina.common.IoSession; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * Buffers any received messages until join completes. + * + */ +class InductionBuffer +{ + private final Queue _buffer = new LinkedList(); + private final MessageHandler _handler; + private boolean _buffering = true; + + InductionBuffer(MessageHandler handler) + { + _handler = handler; + } + + private void process() throws Exception + { + for (Message o = _buffer.poll(); o != null; o = _buffer.poll()) + { + o.deliver(_handler); + } + _buffering = false; + } + + synchronized void deliver() throws Exception + { + process(); + } + + synchronized void receive(IoSession session, Object msg) throws Exception + { + if (_buffering) + { + _buffer.offer(new Message(session, msg)); + } + else + { + _handler.deliver(session, msg); + } + } + + private static class Message + { + private final IoSession _session; + private final Object _msg; + + Message(IoSession session, Object msg) + { + _session = session; + _msg = msg; + } + + void deliver(MessageHandler handler) throws Exception + { + handler.deliver(_session, _msg); + } + } + + static interface MessageHandler + { + public void deliver(IoSession session, Object msg) throws Exception; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.java new file mode 100644 index 0000000000..5f92aa2971 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/JoinState.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.cluster; + +public enum JoinState +{ + UNINITIALISED, JOINING, INITIATION, INDUCTION, JOINED +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java new file mode 100644 index 0000000000..13465a8615 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/LoadTable.java @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * Maintains loading information about the local member and its cluster peers. + * + */ +public class LoadTable +{ + private final Map _peers = new HashMap(); + private final PriorityQueue _loads = new PriorityQueue(); + private final Loading _local = new Loading(null); + + public LoadTable() + { + _loads.add(_local); + } + + public void setLoad(Member member, long load) + { + synchronized (_peers) + { + Loading loading = _peers.get(member); + if (loading == null) + { + loading = new Loading(member); + synchronized (_loads) + { + _loads.add(loading); + } + _peers.put(member, loading); + } + loading.load = load; + } + } + + public void incrementLocalLoad() + { + synchronized (_local) + { + _local.load++; + } + } + + public void decrementLocalLoad() + { + synchronized (_local) + { + _local.load--; + } + } + + public long getLocalLoad() + { + synchronized (_local) + { + return _local.load; + } + } + + public Member redirect() + { + synchronized (_loads) + { + return _loads.peek().member; + } + } + + private static class Loading implements Comparable + { + private final Member member; + private long load; + + Loading(Member member) + { + this.member = member; + } + + public int compareTo(Object o) + { + return (int) (load - ((Loading) o).load); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.java new file mode 100644 index 0000000000..15752353d1 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Main.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.server.cluster; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +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.log4j.Logger; +import org.apache.mina.common.IoAcceptor; +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 org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.server.transport.ConnectorConfiguration; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; + +/** + * TODO: This is a cut-and-paste from the original broker Main class. Would be preferrable to make that class more + * reuseable to avoid all this duplication. + */ +public class Main extends org.apache.qpid.server.Main +{ + private static final Logger _logger = Logger.getLogger(Main.class); + + protected Main(String[] args) + { + super(args); + } + + protected void setOptions(Options otions) + { + super.setOptions(options); + + //extensions: + Option join = OptionBuilder.withArgName("join").hasArg().withDescription("Join the specified cluster member. Overrides any value in the config file"). + withLongOpt("join").create("j"); + options.addOption(join); + } + + protected void bind(int port, ConnectorConfiguration connectorConfig) + { + try + { + IoAcceptor acceptor = new SocketAcceptor(); + SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig(); + SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig(); + + sc.setReceiveBufferSize(connectorConfig.socketReceiveBufferSize); + sc.setSendBufferSize(connectorConfig.socketWriteBuferSize); + sc.setTcpNoDelay(true); + + // 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()); + } + + String host = InetAddress.getLocalHost().getHostName(); + ClusteredProtocolHandler handler = new ClusteredProtocolHandler(new InetSocketAddress(host, port)); + if (!connectorConfig.enableSSL) + { + acceptor.bind(new InetSocketAddress(port), handler, sconfig); + _logger.info("Qpid.AMQP listening on non-SSL port " + port); + handler.connect(commandLine.getOptionValue("j")); + } + else + { + ClusteredProtocolHandler sslHandler = new ClusteredProtocolHandler(handler); + acceptor.bind(new InetSocketAddress(connectorConfig.sslPort), sslHandler, sconfig); + _logger.info("Qpid.AMQP listening on SSL port " + connectorConfig.sslPort); + } + } + catch (IOException e) + { + _logger.error("Unable to bind service to registry: " + e, e); + } + catch (Exception e) + { + _logger.error("Unable to connect to cluster: " + e, e); + } + } + + public static void main(String[] args) + { + new Main(args); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.java new file mode 100644 index 0000000000..3fbdfdde70 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Member.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.server.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; + +public interface Member extends MemberHandle +{ + public void send(AMQDataBlock data) throws AMQException; + + public void addFailureListener(MemberFailureListener listener); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.java new file mode 100644 index 0000000000..7ce45dffaa --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberFailureListener.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.cluster; + +interface MemberFailureListener +{ + public void failed(MemberHandle member); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.java new file mode 100644 index 0000000000..b8099a12f7 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MemberHandle.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.cluster; + +import org.apache.qpid.framing.AMQShortString; + +public interface MemberHandle +{ + public String getHost(); + + public int getPort(); + + public boolean matches(MemberHandle m); + + public boolean matches(String host, int port); + + public AMQShortString getDetails(); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.java new file mode 100644 index 0000000000..591e652e32 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MembershipChangeListener.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.cluster; + +import java.util.List; + +public interface MembershipChangeListener +{ + public void changed(List members); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.java new file mode 100644 index 0000000000..a83f034021 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandler.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; + +interface MethodHandler +{ + public void handle(int channel, AMQMethodBody method) throws AMQException; +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.java new file mode 100644 index 0000000000..9bf04f5458 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerFactory.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.cluster; + +import org.apache.qpid.server.state.AMQState; + +public interface MethodHandlerFactory +{ + public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.java new file mode 100644 index 0000000000..748a660bb8 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MethodHandlerRegistry.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.cluster; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.state.StateAwareMethodListener; + +import java.util.HashMap; +import java.util.Map; + +public class MethodHandlerRegistry +{ + private final Map, StateAwareMethodListener> registry = + new HashMap, StateAwareMethodListener>(); + + public > MethodHandlerRegistry addHandler(B type, StateAwareMethodListener handler) + { + registry.put(type, handler); + return this; + } + + public StateAwareMethodListener getHandler(B frame) + { + return (StateAwareMethodListener) registry.get(frame.getClass()); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java new file mode 100644 index 0000000000..b01ec491ec --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxy.java @@ -0,0 +1,272 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import org.apache.log4j.Logger; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.IoHandlerAdapter; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.RuntimeIOException; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +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 org.apache.qpid.AMQException; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.client.state.AMQState; +import org.apache.qpid.codec.AMQCodecFactory; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.ConnectionRedirectBody; +import org.apache.qpid.framing.ProtocolInitiation; +import org.apache.qpid.framing.ProtocolVersion; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** + * A 'client stub' for a remote cluster peer, using MINA for IO Layer + * + */ +public class MinaBrokerProxy extends Broker implements MethodHandler +{ + private static final Logger _logger = Logger.getLogger(MinaBrokerProxy.class); + private final ConnectionStatusMonitor _connectionMonitor = new ConnectionStatusMonitor(); + private final ClientHandlerRegistry _legacyHandler; + private final MinaBinding _binding = new MinaBinding(); + private final MemberHandle _local; + private IoSession _session; + private MethodHandler _handler; + private Iterable _replay; + + MinaBrokerProxy(String host, int port, MemberHandle local) + { + super(host, port); + _local = local; + _legacyHandler = new ClientHandlerRegistry(local, null); + } + + private void init(IoSession session) + { + _session = session; + _handler = new ClientAdapter(session, _legacyHandler); + } + + private ConnectFuture connectImpl() + { + _logger.info("Connecting to cluster peer: " + getDetails()); + SocketConnector ioConnector = new SocketConnector(); + SocketConnectorConfig cfg = (SocketConnectorConfig) ioConnector.getDefaultConfig(); + + SocketSessionConfig scfg = (SocketSessionConfig) cfg.getSessionConfig(); + scfg.setTcpNoDelay(true); + scfg.setSendBufferSize(32768); + scfg.setReceiveBufferSize(32768); + InetSocketAddress address = new InetSocketAddress(getHost(), getPort()); + return ioConnector.connect(address, _binding); + } + + //extablish connection without handling redirect + boolean connect() throws IOException, InterruptedException + { + ConnectFuture future = connectImpl(); + // wait for connection to complete + future.join(); + // we call getSession which throws an IOException if there has been an error connecting + try + { + future.getSession(); + } + catch (RuntimeIOException e) + { + _connectionMonitor.failed(e); + _logger.error(new LogMessage("Could not connect to {0}: {1}", this, e), e); + throw e; + } + return _connectionMonitor.waitUntilOpen(); + } + + void connectAsynch(Iterable msgs) + { + _replay = msgs; + connectImpl(); + } + + void replay(Iterable msgs) + { + _replay = msgs; + if(_connectionMonitor.isOpened()) + { + replay(); + } + } + + //establish connection, handling redirect if required... + Broker connectToCluster() throws IOException, InterruptedException + { + connect(); + //wait until the connection is open or get a redirection + if (_connectionMonitor.waitUntilOpen()) + { + return this; + } + else + { + Broker broker = new MinaBrokerProxy(_connectionMonitor.getHost(), _connectionMonitor.getPort(), _local); + broker.connect(); + return broker; + } + } + + public void send(AMQDataBlock data) throws AMQConnectionWaitException + { + if (_session == null) + { + try + { + _connectionMonitor.waitUntilOpen(); + } + catch (InterruptedException e) + { + throw new AMQConnectionWaitException("Failed to send " + data + ": " + e, e); + } + } + _session.write(data); + } + + private void replay() + { + if(_replay != null) + { + for(AMQMethodBody b : _replay) + { + _session.write(new AMQFrame(0, b)); + } + } + } + + public void handle(int channel, AMQMethodBody method) throws AMQException + { + _logger.info(new LogMessage("Handling method: {0} for channel {1}", method, channel)); + if (!handleResponse(channel, method)) + { + _logger.warn(new LogMessage("Unhandled method: {0} for channel {1}", method, channel)); + } + } + + private void handleMethod(int channel, AMQMethodBody method) throws AMQException + { + if (method instanceof ConnectionRedirectBody) + { + //signal redirection to waiting thread + ConnectionRedirectBody redirect = (ConnectionRedirectBody) method; + String[] parts = redirect.host.toString().split(":"); + _connectionMonitor.redirect(parts[0], Integer.parseInt(parts[1])); + } + else + { + _handler.handle(channel, method); + if (AMQState.CONNECTION_OPEN.equals(_legacyHandler.getCurrentState()) && _handler != this) + { + _handler = this; + _logger.info(new LogMessage("Connection opened, handler switched")); + //replay any messages: + replay(); + //signal waiting thread: + _connectionMonitor.opened(); + } + } + } + + private void handleFrame(AMQFrame frame) throws AMQException + { + AMQBody body = frame.getBodyFrame(); + if (body instanceof AMQMethodBody) + { + handleMethod(frame.getChannel(), (AMQMethodBody) body); + } + else + { + throw new AMQUnexpectedBodyTypeException(AMQMethodBody.class, body); + } + } + + public String toString() + { + return "MinaBrokerProxy[" + (_session == null ? super.toString() : _session.getRemoteAddress()) + "]"; + } + + private class MinaBinding extends IoHandlerAdapter + { + public void sessionCreated(IoSession session) throws Exception + { + init(session); + _logger.info(new LogMessage("{0}: created", MinaBrokerProxy.this)); + ProtocolCodecFilter pcf = new ProtocolCodecFilter(new AMQCodecFactory(false)); + session.getFilterChain().addLast("protocolFilter", pcf); + + /* Find last protocol version in protocol version list. Make sure last protocol version + listed in the build file (build-module.xml) is the latest version which will be used + here. */ + + session.write(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); + } + + public void sessionOpened(IoSession session) throws Exception + { + _logger.info(new LogMessage("{0}: opened", MinaBrokerProxy.this)); + } + + public void sessionClosed(IoSession session) throws Exception + { + _logger.info(new LogMessage("{0}: closed", MinaBrokerProxy.this)); + } + + public void exceptionCaught(IoSession session, Throwable throwable) throws Exception + { + _logger.error(new LogMessage("{0}: received {1}", MinaBrokerProxy.this, throwable), throwable); + if (! (throwable instanceof IOException)) + { + _session.close(); + } + failed(); + } + + public void messageReceived(IoSession session, Object object) throws Exception + { + if (object instanceof AMQFrame) + { + handleFrame((AMQFrame) object); + } + else + { + throw new AMQUnexpectedFrameTypeException("Received message of unrecognised type: " + object); + } + } + + public void messageSent(IoSession session, Object object) throws Exception + { + _logger.debug(new LogMessage("{0}: sent {1}", MinaBrokerProxy.this, object)); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.java new file mode 100644 index 0000000000..5e70de7665 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/MinaBrokerProxyFactory.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.cluster; + +public class MinaBrokerProxyFactory implements BrokerFactory +{ + private final MemberHandle _local; + + MinaBrokerProxyFactory(MemberHandle local) + { + _local = local; + } + + public Broker create(MemberHandle handle) + { + return new MinaBrokerProxy(handle.getHost(), handle.getPort(), _local); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.java new file mode 100644 index 0000000000..fe76ca6505 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ResponseHandler.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.cluster; + +import org.apache.qpid.framing.AMQMethodBody; + +public interface ResponseHandler +{ + public void responded(AMQMethodBody response); + + public void removed(); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.java new file mode 100644 index 0000000000..159612331c --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/Sendable.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.cluster; + +import org.apache.qpid.AMQException; + +public interface Sendable +{ + public void send(int channel, Member member) throws AMQException; +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.java new file mode 100644 index 0000000000..aadcfa4b4c --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/ServerHandlerRegistry.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.cluster; + +import org.apache.log4j.Logger; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +//import org.apache.qpid.server.state.IllegalStateTransitionException; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; + +import java.util.HashMap; +import java.util.Map; + +/** + * An extension of server.AMQStateManager that allows different handlers to be registered. + * + */ +class ServerHandlerRegistry extends AMQStateManager +{ + private final Logger _logger = Logger.getLogger(ServerHandlerRegistry.class); + private final Map _handlers = new HashMap(); + + ServerHandlerRegistry(VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession) + { + super(AMQState.CONNECTION_NOT_STARTED, false, virtualHostRegistry, protocolSession); + } + + ServerHandlerRegistry(ServerHandlerRegistry s, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession) + { + this(virtualHostRegistry, protocolSession); + _handlers.putAll(s._handlers); + } + + ServerHandlerRegistry(MethodHandlerFactory factory, VirtualHostRegistry virtualHostRegistry, AMQProtocolSession protocolSession) + { + this(virtualHostRegistry, protocolSession); + init(factory); + } + + void setHandlers(AMQState state, MethodHandlerRegistry handlers) + { + _handlers.put(state, handlers); + } + + void init(MethodHandlerFactory factory) + { + for (AMQState s : AMQState.values()) + { + setHandlers(s, factory.register(s, new MethodHandlerRegistry())); + } + } + + protected StateAwareMethodListener findStateTransitionHandler(AMQState state, B frame) //throws IllegalStateTransitionException + { + MethodHandlerRegistry registry = _handlers.get(state); + StateAwareMethodListener handler = (registry == null) ? null : registry.getHandler(frame); + if (handler == null) + { + _logger.warn(new LogMessage("No handler for {0}, {1}", state, frame)); + } + return handler; + } + + > void addHandler(AMQState state, B type, StateAwareMethodListener handler) + { + MethodHandlerRegistry registry = _handlers.get(state); + if (registry == null) + { + registry = new MethodHandlerRegistry(); + _handlers.put(state, registry); + } + registry.addHandler(type, handler); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.java new file mode 100644 index 0000000000..bd3757bf97 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleBodySendable.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQBody; +import org.apache.qpid.framing.AMQFrame; + +/** + */ +public class SimpleBodySendable implements Sendable +{ + private final AMQBody _body; + + public SimpleBodySendable(AMQBody body) + { + _body = body; + } + + public void send(int channel, Member member) throws AMQException + { + member.send(new AMQFrame(channel, _body)); + } + + public String toString() + { + return _body.toString(); + } + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.java new file mode 100644 index 0000000000..1255094b1d --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleMemberHandle.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.server.cluster; + +import org.apache.qpid.framing.AMQShortString; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +public class SimpleMemberHandle implements MemberHandle +{ + private final String _host; + private final int _port; + + public SimpleMemberHandle(String host, int port) + { + _host = host; + _port = port; + } + + public SimpleMemberHandle(AMQShortString details) + { + this(details.toString()); + } + + public SimpleMemberHandle(String details) + { + String[] parts = details.split(":"); + _host = parts[0]; + _port = Integer.parseInt(parts[1]); + } + + public SimpleMemberHandle(InetSocketAddress address) throws UnknownHostException + { + this(address.getAddress(), address.getPort()); + } + + public SimpleMemberHandle(InetAddress address, int port) throws UnknownHostException + { + this(canonical(address).getHostAddress(), port); + } + + public String getHost() + { + return _host; + } + + public int getPort() + { + return _port; + } + + public int hashCode() + { + return getPort(); + } + + public boolean equals(Object o) + { + return o instanceof MemberHandle && matches((MemberHandle) o); + } + + public boolean matches(MemberHandle m) + { + return matches(m.getHost(), m.getPort()); + } + + public boolean matches(String host, int port) + { + return _host.equals(host) && _port == port; + } + + public AMQShortString getDetails() + { + return new AMQShortString(_host + ":" + _port); + } + + public String toString() + { + return getDetails().toString(); + } + + static List stringToMembers(String membership) + { + String[] names = membership.split("\\s"); + List members = new ArrayList(); + for (String name : names) + { + members.add(new SimpleMemberHandle(name)); + } + return members; + } + + static String membersToString(List members) + { + StringBuffer buffer = new StringBuffer(); + boolean first = true; + for (MemberHandle m : members) + { + if (first) + { + first = false; + } + else + { + buffer.append(" "); + } + buffer.append(m.getDetails()); + } + + return buffer.toString(); + } + + private static InetAddress canonical(InetAddress address) throws UnknownHostException + { + if (address.isLoopbackAddress()) + { + return InetAddress.getLocalHost(); + } + else + { + return address; + } + } + + public MemberHandle resolve() + { + return resolve(this); + } + + public static MemberHandle resolve(MemberHandle handle) + { + try + { + return new SimpleMemberHandle(new InetSocketAddress(handle.getHost(), handle.getPort())); + } + catch (UnknownHostException e) + { + e.printStackTrace(); + return handle; + } + } + + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.java new file mode 100644 index 0000000000..7e5563460f --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/SimpleSendable.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.MethodConverter_8_0; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.server.queue.AMQMessage; + +import java.util.Iterator; + +public class SimpleSendable implements Sendable +{ + + //todo fixme - remove 0-8 hard coding + ProtocolVersionMethodConverter _methodConverter = new MethodConverter_8_0(); + + private final AMQMessage _message; + + public SimpleSendable(AMQMessage message) + { + _message = message; + } + + public void send(int channel, Member member) throws AMQException + { + member.send(new AMQFrame(channel, _methodConverter.convertToBody(_message.getMessagePublishInfo()))); + member.send(new AMQFrame(channel, _message.getContentHeaderBody())); + Iterator it = _message.getContentBodyIterator(); + while (it.hasNext()) + { + member.send(new AMQFrame(channel, _methodConverter.convertToBody(it.next()))); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.java new file mode 100644 index 0000000000..86710e8a31 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChainedClusterMethodHandler.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.cluster.handler; + +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; + +import java.util.List; +import java.util.ArrayList; + +public class ChainedClusterMethodHandler extends ClusterMethodHandler +{ + private final List> _handlers; + + private ChainedClusterMethodHandler() + { + this(new ArrayList>()); + } + + public ChainedClusterMethodHandler(List> handlers) + { + _handlers = handlers; + } + + public ChainedClusterMethodHandler(ClusterMethodHandler... handlers) + { + this(); + for(ClusterMethodHandlerhandler: handlers) + { + _handlers.add(handler); + } + } + + protected final void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + for(ClusterMethodHandler handler : _handlers) + { + handler.peer(stateMgr, evt); + } + } + + protected final void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + for(ClusterMethodHandler handler : _handlers) + { + handler.client(stateMgr, evt); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java new file mode 100644 index 0000000000..c9f6dbfb37 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ChannelQueueManager.java @@ -0,0 +1,136 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster.handler; + +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.log4j.Logger; + +import java.util.Map; +import java.util.HashMap; + +/** + * Maintains the default queue names for a channel, and alters subsequent frames where necessary + * to use this (i.e. when no queue is explictly specified). + * + */ +class ChannelQueueManager +{ + private static final Logger _logger = Logger.getLogger(ChannelQueueManager.class); + private final Map _channelQueues = new HashMap(); + + ClusterMethodHandler createQueueDeclareHandler() + { + return new QueueDeclareHandler(); + } + + ClusterMethodHandler createQueueDeleteHandler() + { + return new QueueDeleteHandler(); + } + + ClusterMethodHandler createQueueBindHandler() + { + return new QueueBindHandler(); + } + + ClusterMethodHandler createBasicConsumeHandler() + { + return new BasicConsumeHandler(); + } + + private void set(int channel, AMQShortString queue) + { + _channelQueues.put(channel, queue); + _logger.info(new LogMessage("Set default queue for {0} to {1}", channel, queue)); + } + + private AMQShortString get(int channel) + { + AMQShortString queue = _channelQueues.get(channel); + _logger.info(new LogMessage("Default queue for {0} is {1}", channel, queue)); + return queue; + } + + private class QueueDeclareHandler extends ClusterMethodHandler + { + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + set(evt.getChannelId(), evt.getMethod().queue); + } + } + private class QueueBindHandler extends ClusterMethodHandler + { + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + if(evt.getMethod().queue == null) + { + evt.getMethod().queue = get(evt.getChannelId()); + } + } + } + private class QueueDeleteHandler extends ClusterMethodHandler + { + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + if(evt.getMethod().queue == null) + { + evt.getMethod().queue = get(evt.getChannelId()); + } + } + } + + private class BasicConsumeHandler extends ClusterMethodHandler + { + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + if(evt.getMethod().queue == null) + { + evt.getMethod().queue = get(evt.getChannelId()); + } + } + } + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.java new file mode 100644 index 0000000000..faab99b0f6 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandler.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.cluster.handler; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.AMQException; + +public abstract class ClusterMethodHandler implements StateAwareMethodListener +{ + public final void methodReceived(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateMgr.getProtocolSession(); + + if (ClusteredProtocolSession.isPeerSession(session)) + { + peer(stateMgr, evt); + } + else + { + client(stateMgr, evt); + } + } + + protected abstract void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException; + protected abstract void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException; +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.java new file mode 100644 index 0000000000..e7509da32a --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ClusterMethodHandlerFactory.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.cluster.ClusterCapability; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.LoadTable; +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.cluster.MethodHandlerFactory; +import org.apache.qpid.server.cluster.MethodHandlerRegistry; +import org.apache.qpid.server.cluster.SimpleMemberHandle; +import org.apache.qpid.server.handler.ChannelCloseHandler; +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.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.ExchangeDeclareHandler; +import org.apache.qpid.server.handler.ExchangeDeleteHandler; +import org.apache.qpid.server.handler.BasicCancelMethodHandler; +import org.apache.qpid.server.handler.BasicPublishMethodHandler; +import org.apache.qpid.server.handler.QueueBindHandler; +import org.apache.qpid.server.handler.QueueDeleteHandler; +import org.apache.qpid.server.handler.BasicQosHandler; +import org.apache.qpid.server.handler.TxSelectHandler; +import org.apache.qpid.server.handler.TxCommitHandler; +import org.apache.qpid.server.handler.TxRollbackHandler; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class ClusterMethodHandlerFactory implements MethodHandlerFactory +{ + private final GroupManager _groupMgr; + private final LoadTable _loadTable; + + public ClusterMethodHandlerFactory(GroupManager groupMgr, LoadTable loadTable) + { + _groupMgr = groupMgr; + _loadTable = loadTable; + } + + public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry) + { + switch (state) + { + case CONNECTION_NOT_STARTED: + return registry.addHandler(ConnectionStartOkBody.class, ConnectionStartOkMethodHandler.getInstance()); + case CONNECTION_NOT_AUTH: + return registry.addHandler(ConnectionSecureOkBody.class, ConnectionSecureOkMethodHandler.getInstance()); + case CONNECTION_NOT_TUNED: + return registry.addHandler(ConnectionTuneOkBody.class, ConnectionTuneOkMethodHandler.getInstance()); + case CONNECTION_NOT_OPENED: + //connection.open override: + return registry.addHandler(ConnectionOpenBody.class, new ConnectionOpenHandler()); + case CONNECTION_OPEN: + return registerConnectionOpened(registry); + } + return registry; + } + + private MethodHandlerRegistry registerConnectionOpened(MethodHandlerRegistry registry) + { + //new cluster method handlers: + registry.addHandler(ClusterJoinBody.class, new JoinHandler()); + registry.addHandler(ClusterLeaveBody.class, new LeaveHandler()); + registry.addHandler(ClusterSuspectBody.class, new SuspectHandler()); + registry.addHandler(ClusterMembershipBody.class, new MembershipHandler()); + registry.addHandler(ClusterPingBody.class, new PingHandler()); + registry.addHandler(ClusterSynchBody.class, new SynchHandler()); + + //connection.close override: + registry.addHandler(ConnectionCloseBody.class, new ConnectionCloseHandler()); + + //replicated handlers: + registry.addHandler(ExchangeDeclareBody.class, replicated(ExchangeDeclareHandler.getInstance())); + registry.addHandler(ExchangeDeleteBody.class, replicated(ExchangeDeleteHandler.getInstance())); + + ChannelQueueManager channelQueueMgr = new ChannelQueueManager(); + + + LocalQueueDeclareHandler handler = new LocalQueueDeclareHandler(_groupMgr); + registry.addHandler(QueueDeclareBody.class, + chain(new QueueNameGenerator(handler), + channelQueueMgr.createQueueDeclareHandler(), + new ReplicatingHandler(_groupMgr, handler))); + + registry.addHandler(QueueBindBody.class, chain(channelQueueMgr.createQueueBindHandler(), replicated(QueueBindHandler.getInstance()))); + registry.addHandler(QueueDeleteBody.class, chain(channelQueueMgr.createQueueDeleteHandler(), replicated(alternate(new QueueDeleteHandler(false), new QueueDeleteHandler(true))))); + registry.addHandler(BasicConsumeBody.class, chain(channelQueueMgr.createBasicConsumeHandler(), new ReplicatingConsumeHandler(_groupMgr))); + + //other modified handlers: + registry.addHandler(BasicCancelBody.class, alternate(new RemoteCancelHandler(), BasicCancelMethodHandler.getInstance())); + + //other unaffected handlers: + registry.addHandler(BasicPublishBody.class, BasicPublishMethodHandler.getInstance()); + registry.addHandler(BasicQosBody.class, BasicQosHandler.getInstance()); + registry.addHandler(ChannelOpenBody.class, ChannelOpenHandler.getInstance()); + registry.addHandler(ChannelCloseBody.class, ChannelCloseHandler.getInstance()); + registry.addHandler(ChannelFlowBody.class, ChannelFlowHandler.getInstance()); + registry.addHandler(TxSelectBody.class, TxSelectHandler.getInstance()); + registry.addHandler(TxCommitBody.class, TxCommitHandler.getInstance()); + registry.addHandler(TxRollbackBody.class, TxRollbackHandler.getInstance()); + + + return registry; + } + + private class SynchHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + _groupMgr.handleSynch(ClusteredProtocolSession.getSessionPeer(stateManager.getProtocolSession())); + } + } + + private class JoinHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + _groupMgr.handleJoin(new SimpleMemberHandle(evt.getMethod().broker)); + } + } + + private class LeaveHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + _groupMgr.handleLeave(new SimpleMemberHandle(evt.getMethod().broker)); + } + } + + private class SuspectHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + _groupMgr.handleSuspect(new SimpleMemberHandle(evt.getMethod().broker)); + } + } + + private class MembershipHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + ClusterMembershipBody body = evt.getMethod(); + _groupMgr.handleMembershipAnnouncement(new String(body.members)); + } + } + + private class PingHandler implements StateAwareMethodListener + { + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + MemberHandle peer = new SimpleMemberHandle(evt.getMethod().broker); + _groupMgr.handlePing(peer, evt.getMethod().load); + if (evt.getMethod().responseRequired) + { + evt.getMethod().load = _loadTable.getLocalLoad(); + stateManager.getProtocolSession().writeFrame(new AMQFrame(evt.getChannelId(), evt.getMethod())); + } + } + } + + private class ConnectionOpenHandler extends ExtendedHandler + { + ConnectionOpenHandler() + { + super(ConnectionOpenMethodHandler.getInstance()); + } + + void postHandle(AMQStateManager stateMgr, AMQMethodEvent evt) + { + AMQShortString capabilities = evt.getMethod().capabilities; + if (ClusterCapability.contains(capabilities)) + { + ClusteredProtocolSession.setSessionPeer(stateMgr.getProtocolSession(), ClusterCapability.getPeer(capabilities)); + } + else + { + _loadTable.incrementLocalLoad(); + } + } + } + + private class ConnectionCloseHandler extends ExtendedHandler + { + ConnectionCloseHandler() + { + super(ConnectionCloseMethodHandler.getInstance()); + } + + void postHandle(AMQStateManager stateMgr, AMQMethodEvent evt) + { + if (!ClusteredProtocolSession.isPeerSession(stateMgr.getProtocolSession())) + { + _loadTable.decrementLocalLoad(); + } + } + } + + private ReplicatingHandler replicated(StateAwareMethodListener handler) + { + return new ReplicatingHandler(_groupMgr, handler); + } + + private StateAwareMethodListener alternate(StateAwareMethodListener peer, StateAwareMethodListener client) + { + return new PeerHandler(peer, client); + } + + private StateAwareMethodListener chain(ClusterMethodHandler... h) + { + return new ChainedClusterMethodHandler(h); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.java new file mode 100644 index 0000000000..a2f62f714b --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ExtendedHandler.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +class ExtendedHandler implements StateAwareMethodListener +{ + private final StateAwareMethodListener _base; + + ExtendedHandler(StateAwareMethodListener base) + { + _base = base; + } + + public void methodReceived(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + preHandle(stateMgr, evt); + _base.methodReceived(stateMgr, evt); + postHandle(stateMgr, evt); + } + + void preHandle(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + void postHandle(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.java new file mode 100644 index 0000000000..0dc7fe00d2 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/HandlerUtils.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.cluster.handler; + +public abstract class HandlerUtils +{ +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.java new file mode 100644 index 0000000000..f01a8349f2 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/LocalQueueDeclareHandler.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.cluster.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.handler.QueueDeclareHandler; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.ClusteredQueue; +import org.apache.qpid.server.queue.PrivateQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.queue.RemoteQueueProxy; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class LocalQueueDeclareHandler extends QueueDeclareHandler +{ + private static final Logger _logger = Logger.getLogger(LocalQueueDeclareHandler.class); + private final GroupManager _groupMgr; + + LocalQueueDeclareHandler(GroupManager groupMgr) + { + _groupMgr = groupMgr; + } + + protected AMQShortString createName() + { + return new AMQShortString(super.createName().toString() + "@" + _groupMgr.getLocal().getDetails()); + } + + protected AMQQueue createQueue(QueueDeclareBody body, VirtualHost virtualHost, AMQProtocolSession session) throws AMQException + { + //is it private or shared: + if (body.exclusive) + { + if (ClusteredProtocolSession.isPeerSession(session)) + { + //need to get peer from the session... + MemberHandle peer = ClusteredProtocolSession.getSessionPeer(session); + _logger.debug(new LogMessage("Creating proxied queue {0} on behalf of {1}", body.queue, peer)); + return new RemoteQueueProxy(peer, _groupMgr, body.queue, body.durable, new AMQShortString(peer.getDetails()), body.autoDelete, virtualHost); + } + else + { + _logger.debug(new LogMessage("Creating local private queue {0}", body.queue)); + return new PrivateQueue(_groupMgr, body.queue, body.durable, session.getContextKey(), body.autoDelete, virtualHost); + } + } + else + { + _logger.debug(new LogMessage("Creating local shared queue {0}", body.queue)); + return new ClusteredQueue(_groupMgr, body.queue, body.durable, null, body.autoDelete, virtualHost); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.java new file mode 100644 index 0000000000..8b0bb4b127 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/NullListener.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class NullListener implements StateAwareMethodListener +{ + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + } +} + diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.java new file mode 100644 index 0000000000..447e51ccd9 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/PeerHandler.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +/** + * Base for implementing handlers that carry out different actions based on whether the method they + * are handling was sent by a peer (i.e. another broker in the cluster) or a client (i.e. an end-user + * application). + * + */ +public class PeerHandler extends ClusterMethodHandler +{ + private final StateAwareMethodListener _peer; + private final StateAwareMethodListener _client; + + PeerHandler(StateAwareMethodListener peer, StateAwareMethodListener client) + { + _peer = peer; + _client = client; + } + + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + _peer.methodReceived(stateMgr, evt); + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + _client.methodReceived(stateMgr, evt); + } + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.java new file mode 100644 index 0000000000..a669171d3c --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/QueueNameGenerator.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; + +/** + * Generates queue names for queues declared with no name. + * + */ +class QueueNameGenerator extends ClusterMethodHandler +{ + private final LocalQueueDeclareHandler _handler; + + QueueNameGenerator(LocalQueueDeclareHandler handler) + { + _handler = handler; + } + + protected void peer(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) + throws AMQException + { + setName(evt.getMethod());//need to set the name before propagating this method + } + + protected void setName(QueueDeclareBody body) + { + if (body.queue == null) + { + body.queue = _handler.createName(); + } + } +} + diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.java new file mode 100644 index 0000000000..f09763e1ad --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteCancelHandler.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.cluster.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.ClusteredQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class RemoteCancelHandler implements StateAwareMethodListener +{ + private final Logger _logger = Logger.getLogger(RemoteCancelHandler.class); + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + + //By convention, consumers setup between brokers use the queue name as the consumer tag: + AMQQueue queue = queueRegistry.getQueue(evt.getMethod().consumerTag); + if (queue instanceof ClusteredQueue) + { + ((ClusteredQueue) queue).removeRemoteSubscriber(ClusteredProtocolSession.getSessionPeer(session)); + } + else + { + _logger.warn("Got remote cancel request for non-clustered queue: " + queue); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.java new file mode 100644 index 0000000000..073b13688c --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/RemoteConsumeHandler.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.server.cluster.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicConsumeOkBody; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.ClusteredQueue; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * Handles consume requests from other cluster members. + * + */ +public class RemoteConsumeHandler implements StateAwareMethodListener +{ + private final Logger _logger = Logger.getLogger(RemoteConsumeHandler.class); + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + AMQQueue queue = queueRegistry.getQueue(evt.getMethod().queue); + if (queue instanceof ClusteredQueue) + { + ((ClusteredQueue) queue).addRemoteSubcriber(ClusteredProtocolSession.getSessionPeer(session)); + // 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(BasicConsumeOkBody.createAMQFrame(evt.getChannelId(), + (byte)8, (byte)0, // AMQP version (major, minor) + evt.getMethod().queue // consumerTag + )); + } + else + { + _logger.warn("Got remote consume request for non-clustered queue: " + queue); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.java new file mode 100644 index 0000000000..897f8e4fb7 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingConsumeHandler.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.server.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.server.cluster.BroadcastPolicy; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.handler.BasicConsumeMethodHandler; +import org.apache.qpid.protocol.AMQMethodEvent; +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.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ReplicatingConsumeHandler extends ReplicatingHandler +{ + ReplicatingConsumeHandler(GroupManager groupMgr) + { + this(groupMgr, null); + } + + ReplicatingConsumeHandler(GroupManager groupMgr, BroadcastPolicy policy) + { + super(groupMgr, base(), policy); + } + + protected void replicate(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + //only replicate if the queue in question is a shared queue + if (isShared(queueRegistry.getQueue(evt.getMethod().queue))) + { + super.replicate(stateManager, evt); + } + else + { + _logger.info(new LogMessage("Handling consume for private queue ({0}) locally", evt.getMethod())); + local(stateManager, evt); + _logger.info(new LogMessage("Handled consume for private queue ({0}) locally", evt.getMethod())); + + } + } + + protected boolean isShared(AMQQueue queue) + { + return queue != null && queue.isShared(); + } + + static StateAwareMethodListener base() + { + return new PeerHandler(peer(), client()); + } + + static StateAwareMethodListener peer() + { + return new RemoteConsumeHandler(); + } + + static StateAwareMethodListener client() + { + return BasicConsumeMethodHandler.getInstance(); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.java new file mode 100644 index 0000000000..888fa4e426 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/ReplicatingHandler.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.cluster.handler; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.cluster.*; +import org.apache.qpid.server.cluster.policy.StandardPolicies; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.List; + +/** + * Basic template for handling methods that should be broadcast to the group and + * processed locally after 'completion' of this broadcast. + * + */ +class ReplicatingHandler extends ClusterMethodHandler implements StandardPolicies +{ + protected static final Logger _logger = Logger.getLogger(ReplicatingHandler.class); + + private final StateAwareMethodListener _base; + private final GroupManager _groupMgr; + private final BroadcastPolicy _policy; + + ReplicatingHandler(GroupManager groupMgr, StateAwareMethodListener base) + { + this(groupMgr, base, null); + } + + ReplicatingHandler(GroupManager groupMgr, StateAwareMethodListener base, BroadcastPolicy policy) + { + _groupMgr = groupMgr; + _base = base; + _policy = policy; + } + + protected void peer(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + ExchangeRegistry exchangeRegistry = virtualHost.getExchangeRegistry(); + QueueRegistry queueRegistry = virtualHost.getQueueRegistry(); + + local(stateManager, evt); + _logger.debug(new LogMessage("Handled {0} locally", evt.getMethod())); + } + + protected void client(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + replicate(stateMgr, evt); + } + + protected void replicate(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + if (_policy == null) + { + //asynch delivery + _groupMgr.broadcast(new SimpleBodySendable(evt.getMethod())); + local(stateMgr, evt); + } + else + { + Callback callback = new Callback(stateMgr, evt); + _groupMgr.broadcast(new SimpleBodySendable(evt.getMethod()), _policy, callback); + } + _logger.debug(new LogMessage("Replicated {0} to peers", evt.getMethod())); + } + + protected void local(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + _base.methodReceived(stateMgr, evt); + } + + private class Callback implements GroupResponseHandler + { + private final AMQStateManager _stateMgr; + private final AMQMethodEvent _evt; + + Callback(AMQStateManager stateMgr, AMQMethodEvent evt) + { + _stateMgr = stateMgr; + _evt = evt; + } + + public void response(List responses, List members) + { + try + { + local(_stateMgr, _evt); + _logger.debug(new LogMessage("Handled {0} locally, in response to completion of replication", _evt.getMethod())); + } + catch (AMQException e) + { + _logger.error(new LogMessage("Error handling {0}:{1}", _evt.getMethod(), e), e); + } + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.java new file mode 100644 index 0000000000..8b0c638d63 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappedListener.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.cluster.handler; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public class WrappedListener implements StateAwareMethodListener +{ + private final StateAwareMethodListener _primary; + private final StateAwareMethodListener _post; + private final StateAwareMethodListener _pre; + + WrappedListener(StateAwareMethodListener primary, StateAwareMethodListener pre, StateAwareMethodListener post) + { + _pre = check(pre); + _post = check(post); + _primary = check(primary); + } + + public void methodReceived(AMQStateManager stateMgr, AMQMethodEvent evt) throws AMQException + { + _pre.methodReceived(stateMgr, evt); + _primary.methodReceived(stateMgr, evt); + _post.methodReceived(stateMgr, evt); + } + + private static StateAwareMethodListener check(StateAwareMethodListener in) + { + return in == null ? new NullListener() : in; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.java new file mode 100644 index 0000000000..5ec3c9660a --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/handler/WrappingMethodHandlerFactory.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.cluster.handler; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.cluster.MethodHandlerFactory; +import org.apache.qpid.server.cluster.MethodHandlerRegistry; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.StateAwareMethodListener; + +public abstract class WrappingMethodHandlerFactory implements MethodHandlerFactory +{ + private final MethodHandlerFactory _delegate; + private final StateAwareMethodListener _pre; + private final StateAwareMethodListener _post; + + protected WrappingMethodHandlerFactory(MethodHandlerFactory delegate, + StateAwareMethodListener pre, + StateAwareMethodListener post) + { + _delegate = delegate; + _pre = pre; + _post = post; + } + + public MethodHandlerRegistry register(AMQState state, MethodHandlerRegistry registry) + { + if (isWrappableState(state)) + { + return wrap(_delegate.register(state, registry), state); + } + else + { + return _delegate.register(state, registry); + } + } + + protected abstract boolean isWrappableState(AMQState state); + + protected abstract Iterable getWrappableFrameTypes(AMQState state); + + private MethodHandlerRegistry wrap(MethodHandlerRegistry registry, AMQState state) + { + for (FrameDescriptor fd : getWrappableFrameTypes(state)) + { + wrap(registry, fd.type, fd.instance); + } + return registry; + } + + private > void wrap(MethodHandlerRegistry r, B type, A frame) + { + r.addHandler(type, new WrappedListener(r.getHandler(frame), _pre, _post)); + } + + protected static class FrameDescriptor> + { + protected final A instance; + protected final B type; + + public FrameDescriptor(B type, A instance) + { + this.instance = instance; + this.type = type; + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.java new file mode 100644 index 0000000000..79cb558ede --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/AsynchBroadcastPolicy.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.server.cluster.policy; + +import org.apache.qpid.server.cluster.BroadcastPolicy; + +public class AsynchBroadcastPolicy implements BroadcastPolicy +{ + public boolean isComplete(int responded, int members) + { + return true; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.java new file mode 100644 index 0000000000..42382c6e7a --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/MajorityResponseBroadcastPolicy.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.server.cluster.policy; + +import org.apache.qpid.server.cluster.BroadcastPolicy; + +public class MajorityResponseBroadcastPolicy implements BroadcastPolicy +{ + public boolean isComplete(int responded, int members) + { + return responded > members / 2; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.java new file mode 100644 index 0000000000..e3072a6a40 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/OneResponseBroadcastPolicy.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.server.cluster.policy; + +import org.apache.qpid.server.cluster.BroadcastPolicy; + +public class OneResponseBroadcastPolicy implements BroadcastPolicy +{ + public boolean isComplete(int responded, int members) + { + return responded > 0; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.java new file mode 100644 index 0000000000..dbaf690d3a --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/StandardPolicies.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.cluster.policy; + +import org.apache.qpid.server.cluster.BroadcastPolicy; + +public interface StandardPolicies +{ + public static final BroadcastPolicy ASYNCH_POLICY = new AsynchBroadcastPolicy(); + public static final BroadcastPolicy SYNCH_POLICY = new SynchBroadcastPolicy(); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.java new file mode 100644 index 0000000000..605b8dd51e --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/policy/SynchBroadcastPolicy.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.server.cluster.policy; + +import org.apache.qpid.server.cluster.BroadcastPolicy; + +public class SynchBroadcastPolicy implements BroadcastPolicy +{ + public boolean isComplete(int responded, int members) + { + return responded == members; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.java new file mode 100644 index 0000000000..3664be58bc --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ChainedMethodRecorder.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.cluster.replay; + +import org.apache.qpid.framing.AMQMethodBody; + +abstract class ChainedMethodRecorder implements MethodRecorder +{ + private final MethodRecorder _recorder; + + ChainedMethodRecorder() + { + this(null); + } + + ChainedMethodRecorder(MethodRecorder recorder) + { + _recorder = recorder; + } + + public final void record(T method) + { + if(!doRecord(method) && _recorder != null) + { + _recorder.record(method); + } + } + + protected abstract boolean doRecord(T method); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java new file mode 100644 index 0000000000..5a433b869b --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ConsumerCounts.java @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster.replay; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.AMQShortString; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; + +class ConsumerCounts +{ + private final Map _counts = new HashMap(); + + synchronized void increment(AMQShortString queue) + { + _counts.put(queue, get(queue) + 1); + } + + synchronized void decrement(AMQShortString queue) + { + _counts.put(queue, get(queue) - 1); + } + + private int get(AMQShortString queue) + { + Integer count = _counts.get(queue); + return count == null ? 0 : count; + } + + synchronized void replay(List messages) + { + for(AMQShortString queue : _counts.keySet()) + { + // 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. + BasicConsumeBody m = new BasicConsumeBody((byte)8, + (byte)0, + BasicConsumeBody.getClazz((byte)8, (byte)0), + BasicConsumeBody.getMethod((byte)8, (byte)0), + null, + queue, + false, + false, + false, + false, + queue, + 0); + m.queue = queue; + m.consumerTag = queue; + replay(m, messages); + } + } + + private void replay(BasicConsumeBody msg, List messages) + { + int count = _counts.get(msg.queue); + for(int i = 0; i < count; i++) + { + messages.add(msg); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java new file mode 100644 index 0000000000..e45810438e --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/MethodRecorder.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster.replay; + +import org.apache.qpid.framing.AMQMethodBody; + +/** + * Abstraction through which a method can be recorded for replay + * + */ +interface MethodRecorder +{ + public void record(T method); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.java new file mode 100644 index 0000000000..4d3fe1dbed --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/RecordingMethodHandlerFactory.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.cluster.replay; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.ExchangeDeclareBody; +import org.apache.qpid.framing.ExchangeDeleteBody; +import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.server.cluster.MethodHandlerFactory; +import org.apache.qpid.server.cluster.MethodHandlerRegistry; +import org.apache.qpid.server.cluster.handler.WrappingMethodHandlerFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; + +import java.util.Arrays; + +public class RecordingMethodHandlerFactory extends WrappingMethodHandlerFactory +{ + // 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. + private final byte major = (byte)8; + private final byte minor = (byte)0; + private final Iterable _frames = Arrays.asList(new FrameDescriptor[] + { + new FrameDescriptor(QueueDeclareBody.class, new QueueDeclareBody(major, minor, QueueDeclareBody.getClazz(major, minor), QueueDeclareBody.getMethod(major, minor),null,false,false,false,false,false,null,0)), + new FrameDescriptor(QueueDeleteBody.class, new QueueDeleteBody(major, minor, QueueDeleteBody.getClazz(major, minor), QueueDeleteBody.getMethod(major, minor),false,false,false,null,0)), + new FrameDescriptor(QueueBindBody.class, new QueueBindBody(major, minor, QueueBindBody.getClazz(major, minor), QueueBindBody.getMethod(major, minor),null,null,false,null,null,0)), + new FrameDescriptor(ExchangeDeclareBody.class, new ExchangeDeclareBody(major, minor, ExchangeDeclareBody.getClazz(major, minor), ExchangeDeclareBody.getMethod(major, minor),null,false,false,null,false,false,false,0,null)), + new FrameDescriptor(ExchangeDeleteBody.class, new ExchangeDeleteBody(major, minor, ExchangeDeleteBody.getClazz(major, minor), ExchangeDeleteBody.getMethod(major, minor),null,false,false,0)), + new FrameDescriptor(BasicConsumeBody.class, new BasicConsumeBody(major, minor, BasicConsumeBody.getClazz(major, minor), BasicConsumeBody.getMethod(major, minor),null,null,false,false,false,false,null,0)), + new FrameDescriptor(BasicCancelBody.class, new BasicCancelBody(major, minor, BasicCancelBody.getClazz(major, minor), BasicCancelBody.getMethod(major, minor),null,false)) + }); + + + public RecordingMethodHandlerFactory(MethodHandlerFactory factory, ReplayStore store) + { + super(factory, null, store); + } + + protected boolean isWrappableState(AMQState state) + { + return AMQState.CONNECTION_OPEN.equals(state); + } + + protected Iterable getWrappableFrameTypes(AMQState state) + { + return _frames; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.java new file mode 100644 index 0000000000..898cb80cb3 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayManager.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.cluster.replay; + +import org.apache.qpid.server.cluster.Sendable; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQMethodBody; + +import java.util.List; + +/** + * Abstraction of a replay strategy for use in getting joining members up to + * date with respect to cluster state. + * + */ +public interface ReplayManager +{ + public List replay(boolean isLeader); +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java new file mode 100644 index 0000000000..d7bbb1c36b --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/replay/ReplayStore.java @@ -0,0 +1,311 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster.replay; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.*; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.cluster.util.Bindings; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.protocol.AMQMethodEvent; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Stores method invocations for replay to new members. + * + */ +public class ReplayStore implements ReplayManager, StateAwareMethodListener +{ + private static final Logger _logger = Logger.getLogger(ReplayStore.class); + + private final Map, MethodRecorder> _globalRecorders = new HashMap, MethodRecorder>(); + private final Map, MethodRecorder> _localRecorders = new HashMap, MethodRecorder>(); + private final Map _sharedQueues = new ConcurrentHashMap(); + private final Map _privateQueues = new ConcurrentHashMap(); + private final Bindings _sharedBindings = new Bindings(); + private final Bindings _privateBindings = new Bindings(); + private final Map _exchanges = new ConcurrentHashMap(); + private final ConsumerCounts _consumers = new ConsumerCounts(); + + public ReplayStore() + { + _globalRecorders.put(QueueDeclareBody.class, new SharedQueueDeclareRecorder()); + _globalRecorders.put(QueueDeleteBody.class, new SharedQueueDeleteRecorder()); + _globalRecorders.put(QueueBindBody.class, new SharedQueueBindRecorder()); + _globalRecorders.put(ExchangeDeclareBody.class, new ExchangeDeclareRecorder()); + _globalRecorders.put(ExchangeDeleteBody.class, new ExchangeDeleteRecorder()); + + _localRecorders.put(QueueDeclareBody.class, new PrivateQueueDeclareRecorder()); + _localRecorders.put(QueueDeleteBody.class, new PrivateQueueDeleteRecorder()); + _localRecorders.put(QueueBindBody.class, new PrivateQueueBindRecorder()); + _localRecorders.put(BasicConsumeBody.class, new BasicConsumeRecorder()); + _localRecorders.put(BasicCancelBody.class, new BasicCancelRecorder()); + _localRecorders.put(ExchangeDeclareBody.class, new ExchangeDeclareRecorder()); + _localRecorders.put(ExchangeDeleteBody.class, new ExchangeDeleteRecorder()); + } + + public void methodReceived(AMQStateManager stateManager, AMQMethodEvent evt) throws AMQException + { + AMQProtocolSession session = stateManager.getProtocolSession(); + VirtualHost virtualHost = session.getVirtualHost(); + + _logger.debug(new LogMessage("Replay store received {0}", evt.getMethod())); + AMQMethodBody request = evt.getMethod(); + + //allow any (relevant) recorder registered for this type of request to record it: + MethodRecorder recorder = getRecorders(session).get(request.getClass()); + if (recorder != null) + { + recorder.record(request); + } + } + + private Map, MethodRecorder> getRecorders(AMQProtocolSession session) + { + if (ClusteredProtocolSession.isPeerSession(session)) + { + return _globalRecorders; + } + else + { + return _localRecorders; + } + } + + public List replay(boolean isLeader) + { + List methods = new ArrayList(); + methods.addAll(_exchanges.values()); + methods.addAll(_privateQueues.values()); + synchronized(_privateBindings) + { + methods.addAll(_privateBindings.values()); + } + if (isLeader) + { + methods.addAll(_sharedQueues.values()); + synchronized(_sharedBindings) + { + methods.addAll(_sharedBindings.values()); + } + } + _consumers.replay(methods); + // 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. + methods.add(new ClusterSynchBody((byte)8, (byte)0, ClusterSynchBody.getClazz((byte)8, (byte)0), ClusterSynchBody.getMethod((byte)8, (byte)0))); + return methods; + } + + private class BasicConsumeRecorder implements MethodRecorder + { + public void record(BasicConsumeBody method) + { + if(_sharedQueues.containsKey(method.queue)) + { + _consumers.increment(method.queue); + } + } + } + + private class BasicCancelRecorder implements MethodRecorder + { + public void record(BasicCancelBody method) + { + if(_sharedQueues.containsKey(method.consumerTag)) + { + _consumers.decrement(method.consumerTag); + } + } + } + + private class SharedQueueDeclareRecorder extends QueueDeclareRecorder + { + SharedQueueDeclareRecorder() + { + super(false, _sharedQueues); + } + } + + private class PrivateQueueDeclareRecorder extends QueueDeclareRecorder + { + PrivateQueueDeclareRecorder() + { + super(true, _privateQueues, new SharedQueueDeclareRecorder()); + } + } + + private class SharedQueueDeleteRecorder extends QueueDeleteRecorder + { + SharedQueueDeleteRecorder() + { + super(_sharedQueues, _sharedBindings); + } + } + + private class PrivateQueueDeleteRecorder extends QueueDeleteRecorder + { + PrivateQueueDeleteRecorder() + { + super(_privateQueues, _privateBindings, new SharedQueueDeleteRecorder()); + } + } + + private class SharedQueueBindRecorder extends QueueBindRecorder + { + SharedQueueBindRecorder() + { + super(_sharedQueues, _sharedBindings); + } + } + + private class PrivateQueueBindRecorder extends QueueBindRecorder + { + PrivateQueueBindRecorder() + { + super(_privateQueues, _privateBindings, new SharedQueueBindRecorder()); + } + } + + + private static class QueueDeclareRecorder extends ChainedMethodRecorder + { + private final boolean _exclusive; + private final Map _queues; + + QueueDeclareRecorder(boolean exclusive, Map queues) + { + _queues = queues; + _exclusive = exclusive; + } + + QueueDeclareRecorder(boolean exclusive, Map queues, QueueDeclareRecorder recorder) + { + super(recorder); + _queues = queues; + _exclusive = exclusive; + } + + + protected boolean doRecord(QueueDeclareBody method) + { + if (_exclusive == method.exclusive) + { + _queues.put(method.queue, method); + return true; + } + else + { + return false; + } + } + } + + private class QueueDeleteRecorder extends ChainedMethodRecorder + { + private final Map _queues; + private final Bindings _bindings; + + QueueDeleteRecorder(Map queues, Bindings bindings) + { + this(queues, bindings, null); + } + + QueueDeleteRecorder(Map queues, Bindings bindings, QueueDeleteRecorder recorder) + { + super(recorder); + _queues = queues; + _bindings = bindings; + } + + protected boolean doRecord(QueueDeleteBody method) + { + if (_queues.remove(method.queue) != null) + { + _bindings.unbind1(method.queue); + return true; + } + else + { + return false; + } + } + } + + private class QueueBindRecorder extends ChainedMethodRecorder + { + private final Map _queues; + private final Bindings _bindings; + + QueueBindRecorder(Map queues, Bindings bindings) + { + _queues = queues; + _bindings = bindings; + } + + QueueBindRecorder(Map queues, Bindings bindings, QueueBindRecorder recorder) + { + super(recorder); + _queues = queues; + _bindings = bindings; + } + + protected boolean doRecord(QueueBindBody method) + { + if (_queues.containsKey(method.queue)) + { + _bindings.bind(method.queue, method.exchange, method); + return true; + } + else + { + return false; + } + } + } + + private class ExchangeDeclareRecorder implements MethodRecorder + { + public void record(ExchangeDeclareBody method) + { + _exchanges.put(method.exchange, method); + } + } + + private class ExchangeDeleteRecorder implements MethodRecorder + { + public void record(ExchangeDeleteBody method) + { + _exchanges.remove(method.exchange); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java new file mode 100644 index 0000000000..49de0a7cbf --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/Bindings.java @@ -0,0 +1,83 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +/** + * Maps two separate keys to a list of values. + * + */ +public class Bindings +{ + private final MultiValuedMap> _a = new MultiValuedMap>(); + private final MultiValuedMap> _b = new MultiValuedMap>(); + private final Collection _values = new HashSet(); + + public void bind(K1 key1, K2 key2, V value) + { + _a.add(key1, new Binding(key2, value)); + _b.add(key2, new Binding(key1, value)); + _values.add(value); + } + + public void unbind1(K1 key1) + { + Collection> values = _a.remove(key1); + for (Binding v : values) + { + _b.remove(v.key); + _values.remove(v.value); + } + } + + public void unbind2(K2 key2) + { + Collection> values = _b.remove(key2); + for (Binding v : values) + { + _a.remove(v.key); + _values.remove(v.value); + } + } + + public Collection values() + { + return Collections.unmodifiableCollection(_values); + } + + /** + * Value needs to hold key to the other map + */ + private class Binding + { + private final T key; + private final V value; + + Binding(T key, V value) + { + this.key = key; + this.value = value; + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.java new file mode 100644 index 0000000000..406fe45701 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/InvokeMultiple.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.cluster.util; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Set; +import java.util.HashSet; + +/** + * Allows a method to be invoked on a list of listeners with one call + * + */ +public class InvokeMultiple implements InvocationHandler +{ + private final Set _targets = new HashSet(); + private final T _proxy; + + public InvokeMultiple(Class type) + { + _proxy = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, this); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + Set targets; + synchronized(this) + { + targets = new HashSet(_targets); + } + + for(T target : targets) + { + method.invoke(target, args); + } + return null; + } + + public synchronized void addListener(T t) + { + _targets.add(t); + } + + public synchronized void removeListener(T t) + { + _targets.remove(t); + } + + public T getProxy() + { + return _proxy; + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.java new file mode 100644 index 0000000000..9be90298ea --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/LogMessage.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.cluster.util; + +import java.text.MessageFormat; + +/** + * Convenience class to allow log messages to be specified in terms + * of MessageFormat patterns with a variable set of parameters. The + * production of the string is only done if toSTring is called so it + * works well with debug level messages, allowing complex messages + * to be specified that are only evaluated if actually printed. + * + */ +public class LogMessage +{ + private final String _message; + private final Object[] _args; + + public LogMessage(String message) + { + this(message, new Object[0]); + } + + public LogMessage(String message, Object... args) + { + _message = message; + _args = args; + } + + public String toString() + { + return MessageFormat.format(_message, _args); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.java new file mode 100644 index 0000000000..ebe1fe47dd --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/cluster/util/MultiValuedMap.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.cluster.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Maps a key to a collection of values + * + */ +public class MultiValuedMap +{ + private Map> _map = new HashMap>(); + + public boolean add(K key, V value) + { + Collection values = get(key); + if (values == null) + { + values = createList(); + _map.put(key, values); + } + return values.add(value); + } + + public Collection get(K key) + { + return _map.get(key); + } + + public Collection remove(K key) + { + return _map.remove(key); + } + + protected Collection createList() + { + return new ArrayList(); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java new file mode 100644 index 0000000000..9fa96ece1e --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredQueue.java @@ -0,0 +1,175 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicCancelBody; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.cluster.*; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.store.StoreContext; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents a shared queue in a cluster. The key difference is that as well as any + * local consumers, there may be consumers for this queue on other members of the + * cluster. + * + */ +public class ClusteredQueue extends AMQQueue +{ + private static final Logger _logger = Logger.getLogger(ClusteredQueue.class); + private final ConcurrentHashMap _peers = new ConcurrentHashMap(); + private final GroupManager _groupMgr; + private final NestedSubscriptionManager _subscriptions; + + public ClusteredQueue(GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + throws AMQException + { + super(name, durable, owner, autoDelete, virtualHost, new ClusteredSubscriptionManager()); + _groupMgr = groupMgr; + _subscriptions = ((ClusteredSubscriptionManager) getSubscribers()).getAllSubscribers(); + } + + + public void process(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException + { + _logger.info(new LogMessage("{0} delivered to clustered queue {1}", msg, this)); + super.process(storeContext, msg, deliverFirst); + } + + protected void autodelete() throws AMQException + { + if(!_subscriptions.hasActiveSubscribers()) + { + //delete locally: + delete(); + + //send deletion request to all other members: + // 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. + QueueDeleteBody request = new QueueDeleteBody((byte)8, + (byte)0, + QueueDeleteBody.getClazz((byte)8,(byte)0), + QueueDeleteBody.getMethod((byte)8,(byte)0), + false, + false, + false, + getName(), + 0); + + _groupMgr.broadcast(new SimpleBodySendable(request)); + } + } + + public void unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException + { + //handle locally: + super.unregisterProtocolSession(ps, channel, consumerTag); + + //signal other members: + // 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. + BasicCancelBody request = new BasicCancelBody((byte)8, + (byte)0, + BasicCancelBody.getClazz((byte)8, (byte)0), + BasicCancelBody.getMethod((byte)8, (byte)0), + getName(), + false); + + _groupMgr.broadcast(new SimpleBodySendable(request)); + } + + public void addRemoteSubcriber(MemberHandle peer) + { + _logger.info(new LogMessage("Added remote subscriber for {0} to clustered queue {1}", peer, this)); + //find (or create) a matching subscriber for the peer then increment the count + getSubscriber(key(peer), true).increment(); + } + + public void removeRemoteSubscriber(MemberHandle peer) + { + //find a matching subscriber for the peer then decrement the count + //if count is now zero, remove the subscriber + SimpleMemberHandle key = key(peer); + RemoteSubscriptionImpl s = getSubscriber(key, true); + if (s == null) + { + throw new RuntimeException("No subscriber for " + peer); + } + if (s.decrement()) + { + _peers.remove(key); + _subscriptions.removeSubscription(s); + } + } + + public void removeAllRemoteSubscriber(MemberHandle peer) + { + SimpleMemberHandle key = key(peer); + RemoteSubscriptionImpl s = getSubscriber(key, true); + _peers.remove(key); + _subscriptions.removeSubscription(s); + } + + private RemoteSubscriptionImpl getSubscriber(SimpleMemberHandle key, boolean create) + { + RemoteSubscriptionImpl s = _peers.get(key); + if (s == null && create) + { + return addSubscriber(key, new RemoteSubscriptionImpl(_groupMgr, key)); + } + else + { + return s; + } + } + + private RemoteSubscriptionImpl addSubscriber(SimpleMemberHandle key, RemoteSubscriptionImpl s) + { + RemoteSubscriptionImpl other = _peers.putIfAbsent(key, s); + if (other == null) + { + _subscriptions.addSubscription(s); + new SubscriberCleanup(key, this, _groupMgr); + return s; + } + else + { + return other; + } + } + + private SimpleMemberHandle key(MemberHandle peer) + { + return peer instanceof SimpleMemberHandle ? (SimpleMemberHandle) peer : (SimpleMemberHandle) SimpleMemberHandle.resolve(peer); + } + + static boolean isFromBroker(AMQMessage msg) + { + return ClusteredProtocolSession.isPayloadFromPeer(msg); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.java new file mode 100644 index 0000000000..39ae7e3c3e --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ClusteredSubscriptionManager.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.server.queue; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.cluster.util.LogMessage; + +import java.util.List; + +class ClusteredSubscriptionManager extends SubscriptionSet +{ + private static final Logger _logger = Logger.getLogger(ClusteredSubscriptionManager.class); + private final NestedSubscriptionManager _all; + + ClusteredSubscriptionManager() + { + this(new NestedSubscriptionManager()); + } + + private ClusteredSubscriptionManager(NestedSubscriptionManager all) + { + _all = all; + _all.addSubscription(new Parent()); + } + + NestedSubscriptionManager getAllSubscribers() + { + return _all; + } + + public boolean hasActiveSubscribers() + { + return _all.hasActiveSubscribers(); + } + + public Subscription nextSubscriber(AMQMessage msg) + { + if(ClusteredQueue.isFromBroker(msg)) + { + //if message is from another broker, it should only be delivered + //to another client to meet ordering constraints + Subscription s = super.nextSubscriber(msg); + _logger.info(new LogMessage("Returning next *client* subscriber {0}", s)); + if(s == null) + { + //TODO: deliver to another broker, but set the redelivered flag on the msg + //(this should be policy based) + + //for now just don't deliver it + return null; + } + else + { + return s; + } + } + Subscription s = _all.nextSubscriber(msg); + _logger.info(new LogMessage("Returning next subscriber {0}", s)); + return s; + } + + private class Parent implements WeightedSubscriptionManager + { + public int getWeight() + { + return ClusteredSubscriptionManager.this.getWeight(); + } + + public List getSubscriptions() + { + return ClusteredSubscriptionManager.super.getSubscriptions(); + } + + public boolean hasActiveSubscribers() + { + return ClusteredSubscriptionManager.super.hasActiveSubscribers(); + } + + public Subscription nextSubscriber(AMQMessage msg) + { + return ClusteredSubscriptionManager.super.nextSubscriber(msg); + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.java new file mode 100644 index 0000000000..0566c5203b --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/NestedSubscriptionManager.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.queue; + +import java.util.List; +import java.util.LinkedList; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Distributes messages among a list of subsscription managers, using their + * weighting. + */ +class NestedSubscriptionManager implements SubscriptionManager +{ + private final List _subscribers = new CopyOnWriteArrayList(); + private int _iterations; + private int _index; + + void addSubscription(WeightedSubscriptionManager s) + { + _subscribers.add(s); + } + + void removeSubscription(WeightedSubscriptionManager s) + { + _subscribers.remove(s); + } + + + public List getSubscriptions() + { + List allSubs = new LinkedList(); + + for (WeightedSubscriptionManager subMans : _subscribers) + { + allSubs.addAll(subMans.getSubscriptions()); + } + + return allSubs; + } + + public boolean hasActiveSubscribers() + { + for (WeightedSubscriptionManager s : _subscribers) + { + if (s.hasActiveSubscribers()) + { + return true; + } + } + return false; + } + + public Subscription nextSubscriber(AMQMessage msg) + { + WeightedSubscriptionManager start = current(); + for (WeightedSubscriptionManager s = start; s != null; s = next(start)) + { + if (hasMore(s)) + { + return nextSubscriber(s); + } + } + return null; + } + + private Subscription nextSubscriber(WeightedSubscriptionManager s) + { + _iterations++; + return s.nextSubscriber(null); + } + + private WeightedSubscriptionManager current() + { + return _subscribers.isEmpty() ? null : _subscribers.get(_index); + } + + private boolean hasMore(WeightedSubscriptionManager s) + { + return _iterations < s.getWeight(); + } + + private WeightedSubscriptionManager next(WeightedSubscriptionManager start) + { + WeightedSubscriptionManager s = next(); + return s == start && !hasMore(s) ? null : s; + } + + private WeightedSubscriptionManager next() + { + _iterations = 0; + if (++_index >= _subscribers.size()) + { + _index = 0; + } + return _subscribers.get(_index); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.java new file mode 100644 index 0000000000..f8e4311a77 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/PrivateQueue.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.server.queue; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.cluster.SimpleSendable; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.SimpleBodySendable; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.framing.QueueDeleteBody; +import org.apache.qpid.framing.AMQShortString; + +import java.util.concurrent.Executor; + +/** + * Used to represent a private queue held locally. + * + */ +public class PrivateQueue extends AMQQueue +{ + private final GroupManager _groupMgr; + + public PrivateQueue(GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + throws AMQException + { + super(name, durable, owner, autoDelete, virtualHost); + _groupMgr = groupMgr; + + } + + protected void autodelete() throws AMQException + { + //delete locally: + super.autodelete(); + + //send delete request to peers: + // 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. + QueueDeleteBody request = new QueueDeleteBody((byte)8, (byte)0, + QueueDeleteBody.getClazz((byte)8, (byte)0), + QueueDeleteBody.getMethod((byte)8, (byte)0), + false,false,false,null,0); + request.queue = getName(); + _groupMgr.broadcast(new SimpleBodySendable(request)); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.java new file mode 100644 index 0000000000..efc0540c18 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/ProxiedQueueCleanup.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.queue; + +import org.apache.qpid.server.cluster.MembershipChangeListener; +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; + +import java.util.List; + +class ProxiedQueueCleanup implements MembershipChangeListener +{ + private static final Logger _logger = Logger.getLogger(ProxiedQueueCleanup.class); + + private final MemberHandle _subject; + private final RemoteQueueProxy _queue; + + ProxiedQueueCleanup(MemberHandle subject, RemoteQueueProxy queue) + { + _subject = subject; + _queue = queue; + } + + public void changed(List members) + { + if(!members.contains(_subject)) + { + try + { + _queue.delete(); + _logger.info(new LogMessage("Deleted {0} in response to exclusion of {1}", _queue, _subject)); + } + catch (AMQException e) + { + _logger.info(new LogMessage("Failed to delete {0} in response to exclusion of {1}: {2}", _queue, _subject, e), e); + } + } + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java new file mode 100644 index 0000000000..2a83d65ae5 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteQueueProxy.java @@ -0,0 +1,91 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.cluster.ClusteredProtocolSession; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.cluster.SimpleSendable; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.qpid.server.virtualhost.VirtualHost; + +/** + * TODO: separate out an abstract base class from AMQQueue from which this inherits. It does + * not require all the functionality currently in AMQQueue. + * + */ +public class RemoteQueueProxy extends AMQQueue +{ + private static final Logger _logger = Logger.getLogger(RemoteQueueProxy.class); + private final MemberHandle _target; + private final GroupManager _groupMgr; + + public RemoteQueueProxy(MemberHandle target, GroupManager groupMgr, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost) + throws AMQException + { + super(name, durable, owner, autoDelete, virtualHost); + _target = target; + _groupMgr = groupMgr; + _groupMgr.addMemberhipChangeListener(new ProxiedQueueCleanup(target, this)); + } + + + public void deliver(AMQMessage msg) throws NoConsumersException + { + if (ClusteredProtocolSession.canRelay(msg, _target)) + { + try + { + _logger.debug(new LogMessage("Relaying {0} to {1}", msg, _target)); + relay(msg); + } + catch (NoConsumersException e) + { + throw e; + } + catch (AMQException e) + { + //TODO: sort out exception handling... + e.printStackTrace(); + } + } + else + { + _logger.debug(new LogMessage("Cannot relay {0} to {1}", msg, _target)); + } + } + + void relay(AMQMessage msg) throws AMQException + { + // TODO FIXME - can no longer update the publish body as it is an opaque wrapper object + // if cluster can handle immediate then it should wrap the wrapper... + +// BasicPublishBody publish = msg.getMessagePublishInfo(); +// publish.immediate = false; //can't as yet handle the immediate flag in a cluster + + // send this on to the broker for which it is acting as proxy: + _groupMgr.send(_target, new SimpleSendable(msg)); + } +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java new file mode 100644 index 0000000000..e396432cea --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/RemoteSubscriptionImpl.java @@ -0,0 +1,176 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.SimpleSendable; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.AMQException; + +import java.util.Queue; +import java.util.List; + +class RemoteSubscriptionImpl implements Subscription, WeightedSubscriptionManager +{ + private final GroupManager _groupMgr; + private final MemberHandle _peer; + private boolean _suspended; + private int _count; + + RemoteSubscriptionImpl(GroupManager groupMgr, MemberHandle peer) + { + _groupMgr = groupMgr; + _peer = peer; + } + + synchronized void increment() + { + _count++; + } + + synchronized boolean decrement() + { + return --_count <= 0; + } + + public void send(AMQMessage msg, AMQQueue queue) + { + try + { + _groupMgr.send(_peer, new SimpleSendable(msg)); + } + catch (AMQException e) + { + //TODO: handle exceptions properly... + e.printStackTrace(); + } + } + + public synchronized void setSuspended(boolean suspended) + { + _suspended = suspended; + } + + public synchronized boolean isSuspended() + { + return _suspended; + } + + public synchronized int getWeight() + { + return _count; + } + + public List getSubscriptions() + { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean hasActiveSubscribers() + { + return getWeight() == 0; + } + + public Subscription nextSubscriber(AMQMessage msg) + { + return this; + } + + public void queueDeleted(AMQQueue queue) + { + if (queue instanceof ClusteredQueue) + { + ((ClusteredQueue) queue).removeAllRemoteSubscriber(_peer); + } + } + + public boolean filtersMessages() + { + return false; + } + + public boolean hasInterest(AMQMessage msg) + { + return true; + } + + public Queue getPreDeliveryQueue() + { + return null; + } + + public Queue getResendQueue() + { + return null; + } + + public Queue getNextQueue(Queue messages) + { + return messages; + } + + public void enqueueForPreDelivery(AMQMessage msg, boolean deliverFirst) + { + //no-op -- if selectors are implemented on RemoteSubscriptions then look at SubscriptionImpl + } + + public boolean isAutoClose() + { + return false; + } + + public void close() + { + //no-op + } + + public boolean isClosed() + { + return false; + } + + public boolean isBrowser() + { + return false; + } + + public boolean wouldSuspend(AMQMessage msg) + { + return _suspended; + } + + public void addToResendQueue(AMQMessage msg) + { + //no-op + } + + public Object getSendLock() + { + return new Object(); + } + + public AMQChannel getChannel() + { + return null; + } + +} diff --git a/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.java new file mode 100644 index 0000000000..cc951a4709 --- /dev/null +++ b/Final2/java/cluster/src/main/java/org/apache/qpid/server/queue/SubscriberCleanup.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 org.apache.qpid.server.cluster.MembershipChangeListener; +import org.apache.qpid.server.cluster.MemberHandle; +import org.apache.qpid.server.cluster.GroupManager; +import org.apache.qpid.server.cluster.util.LogMessage; +import org.apache.log4j.Logger; + +import java.util.List; + +class SubscriberCleanup implements MembershipChangeListener +{ + private static final Logger _logger = Logger.getLogger(SubscriberCleanup.class); + + private final MemberHandle _subject; + private final ClusteredQueue _queue; + private final GroupManager _manager; + + SubscriberCleanup(MemberHandle subject, ClusteredQueue queue, GroupManager manager) + { + _subject = subject; + _queue = queue; + _manager = manager; + _manager.addMemberhipChangeListener(this); + } + + public void changed(List members) + { + if(!members.contains(_subject)) + { + _queue.removeAllRemoteSubscriber(_subject); + _manager.removeMemberhipChangeListener(this); + _logger.info(new LogMessage("Removed {0} from {1}", _subject, _queue)); + } + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java new file mode 100644 index 0000000000..b91d7140e0 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerGroupTest.java @@ -0,0 +1,270 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.util.Arrays; + +public class BrokerGroupTest extends TestCase +{ + private final MemberHandle a = new SimpleMemberHandle("A", 1); + private final MemberHandle b = new SimpleMemberHandle("B", 1); + private final MemberHandle c = new SimpleMemberHandle("C", 1); + private final MemberHandle d = new SimpleMemberHandle("D", 1); + + //join (new members perspective) + // (i) connectToLeader() + // ==> check state + // (ii) setMembers() + // ==> check state + // ==> check members + // (iii) synched(leader) + // ==> check state + // ==> check peers + // (iv) synched(other) + // ==> check state + // ==> check peers + // repeat for all others + public void testJoin_newMember() throws Exception + { + MemberHandle[] pre = new MemberHandle[]{a, b, c}; + MemberHandle[] post = new MemberHandle[]{a, b, c}; + + BrokerGroup group = new BrokerGroup(d, new TestReplayManager(), new TestBrokerFactory()); + assertEquals(JoinState.UNINITIALISED, group.getState()); + //(i) + group.connectToLeader(a); + assertEquals(JoinState.JOINING, group.getState()); + assertEquals("Wrong number of peers", 1, group.getPeers().size()); + //(ii) + group.setMembers(Arrays.asList(post)); + assertEquals(JoinState.INITIATION, group.getState()); + assertEquals(Arrays.asList(post), group.getMembers()); + //(iii) & (iv) + for (MemberHandle member : pre) + { + group.synched(member); + if (member == c) + { + assertEquals(JoinState.JOINED, group.getState()); + assertEquals("Wrong number of peers", pre.length, group.getPeers().size()); + } + else + { + assertEquals(JoinState.INDUCTION, group.getState()); + assertEquals("Wrong number of peers", 1, group.getPeers().size()); + } + } + } + + //join (leaders perspective) + // (i) extablish() + // ==> check state + // ==> check members + // ==> check peers + // (ii) connectToProspect() + // ==> check members + // ==> check peers + // repeat (ii) + public void testJoin_Leader() throws IOException, InterruptedException + { + MemberHandle[] prospects = new MemberHandle[]{b, c, d}; + + BrokerGroup group = new BrokerGroup(a, new TestReplayManager(), new TestBrokerFactory()); + assertEquals(JoinState.UNINITIALISED, group.getState()); + //(i) + group.establish(); + assertEquals(JoinState.JOINED, group.getState()); + assertEquals("Wrong number of peers", 0, group.getPeers().size()); + assertEquals("Wrong number of members", 1, group.getMembers().size()); + assertEquals(a, group.getMembers().get(0)); + //(ii) + for (int i = 0; i < prospects.length; i++) + { + group.connectToProspect(prospects[i]); + assertEquals("Wrong number of peers", i + 1, group.getPeers().size()); + for (int j = 0; j <= i; j++) + { + assertTrue(prospects[i].matches(group.getPeers().get(i))); + } + assertEquals("Wrong number of members", i + 2, group.getMembers().size()); + assertEquals(a, group.getMembers().get(0)); + for (int j = 0; j <= i; j++) + { + assertEquals(prospects[i], group.getMembers().get(i + 1)); + } + } + } + + //join (general perspective) + // (i) set up group + // (ii) setMembers() + // ==> check members + // ==> check peers + public void testJoin_general() throws Exception + { + MemberHandle[] view1 = new MemberHandle[]{a, b, c}; + MemberHandle[] view2 = new MemberHandle[]{a, b, c, d}; + MemberHandle[] peers = new MemberHandle[]{a, b, d}; + + BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory()); + //(i) + group.connectToLeader(a); + group.setMembers(Arrays.asList(view1)); + for (MemberHandle h : view1) + { + group.synched(h); + } + //(ii) + group.setMembers(Arrays.asList(view2)); + assertEquals(Arrays.asList(view2), group.getMembers()); + assertEquals(peers.length, group.getPeers().size()); + for (int i = 0; i < peers.length; i++) + { + assertTrue(peers[i].matches(group.getPeers().get(i))); + } + } + + //leadership transfer (valid) + // (i) set up group + // (ii) assumeLeadership() + // ==> check return value + // ==> check members + // ==> check peers + // ==> check isLeader() + // ==> check isLeader(old_leader) + // ==> check isMember(old_leader) + public void testTransferLeadership_valid() throws Exception + { + MemberHandle[] view1 = new MemberHandle[]{a, b}; + MemberHandle[] view2 = new MemberHandle[]{a, b, c, d}; + MemberHandle[] view3 = new MemberHandle[]{b, c, d}; + + BrokerGroup group = new BrokerGroup(b, new TestReplayManager(), new TestBrokerFactory()); + //(i) + group.connectToLeader(a); + group.setMembers(Arrays.asList(view1)); + for (MemberHandle h : view1) + { + group.synched(h); + } + group.setMembers(Arrays.asList(view2)); + //(ii) + boolean result = group.assumeLeadership(); + assertTrue(result); + assertTrue(group.isLeader()); + assertFalse(group.isLeader(a)); + assertEquals(Arrays.asList(view3), group.getMembers()); + assertEquals(2, group.getPeers().size()); + assertTrue(c.matches(group.getPeers().get(0))); + assertTrue(d.matches(group.getPeers().get(1))); + } + + //leadership transfer (invalid) + // (i) set up group + // (ii) assumeLeadership() + // ==> check return value + // ==> check members + // ==> check peers + // ==> check isLeader() + // ==> check isLeader(old_leader) + // ==> check isMember(old_leader) + public void testTransferLeadership_invalid() throws Exception + { + MemberHandle[] view1 = new MemberHandle[]{a, b, c}; + MemberHandle[] view2 = new MemberHandle[]{a, b, c, d}; + + BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory()); + //(i) + group.connectToLeader(a); + group.setMembers(Arrays.asList(view1)); + for (MemberHandle h : view1) + { + group.synched(h); + } + group.setMembers(Arrays.asList(view2)); + //(ii) + boolean result = group.assumeLeadership(); + assertFalse(result); + assertFalse(group.isLeader()); + assertTrue(group.isLeader(a)); + assertEquals(Arrays.asList(view2), group.getMembers()); + assertEquals(3, group.getPeers().size()); + assertTrue(a.matches(group.getPeers().get(0))); + assertTrue(b.matches(group.getPeers().get(1))); + assertTrue(d.matches(group.getPeers().get(2))); + + } + + //leave (leaders perspective) + // (i) set up group + // (ii) remove a member + // ==> check members + // ==> check peers + // ==> check isMember(removed_member) + // repeat (ii) + public void testLeave_leader() + { + MemberHandle[] view1 = new MemberHandle[]{a, b, c, d}; + MemberHandle[] view2 = new MemberHandle[]{a, b, d}; + MemberHandle[] view3 = new MemberHandle[]{a, d}; + MemberHandle[] view4 = new MemberHandle[]{a}; + //(i) + BrokerGroup group = new BrokerGroup(a, new TestReplayManager(), new TestBrokerFactory()); + group.establish(); + group.setMembers(Arrays.asList(view1)); + //(ii) + group.remove(group.findBroker(c, false)); + assertEquals(Arrays.asList(view2), group.getMembers()); + + group.remove(group.findBroker(b, false)); + assertEquals(Arrays.asList(view3), group.getMembers()); + + group.remove(group.findBroker(d, false)); + assertEquals(Arrays.asList(view4), group.getMembers()); + } + + + //leave (general perspective) + // (i) set up group + // (ii) setMember + // ==> check members + // ==> check peers + // ==> check isMember(removed_member) + // repeat (ii) + public void testLeave_general() + { + MemberHandle[] view1 = new MemberHandle[]{a, b, c, d}; + MemberHandle[] view2 = new MemberHandle[]{a, c, d}; + //(i) + BrokerGroup group = new BrokerGroup(c, new TestReplayManager(), new TestBrokerFactory()); + group.establish(); //not strictly the correct way to build up the group, but ok for here + group.setMembers(Arrays.asList(view1)); + //(ii) + group.setMembers(Arrays.asList(view2)); + assertEquals(Arrays.asList(view2), group.getMembers()); + assertEquals(2, group.getPeers().size()); + assertTrue(a.matches(group.getPeers().get(0))); + assertTrue(d.matches(group.getPeers().get(1))); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java new file mode 100644 index 0000000000..f1da312eea --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/BrokerTest.java @@ -0,0 +1,237 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.cluster; + +import junit.framework.TestCase; +import org.apache.mina.common.ByteBuffer; +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.AMQFrameDecodingException; +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.cluster.policy.StandardPolicies; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class BrokerTest extends TestCase +{ + //group request (no failure) + public void testGroupRequest_noFailure() throws AMQException + { + RecordingBroker[] brokers = new RecordingBroker[]{ + new RecordingBroker("A", 1), + new RecordingBroker("B", 2), + new RecordingBroker("C", 3) + }; + GroupResponseValidator handler = new GroupResponseValidator(new TestMethod("response"), new ArrayList(Arrays.asList(brokers))); + GroupRequest grpRequest = new GroupRequest(new SimpleBodySendable(new TestMethod("request")), StandardPolicies.SYNCH_POLICY, handler); + for (Broker b : brokers) + { + b.invoke(grpRequest); + } + grpRequest.finishedSend(); + + for (RecordingBroker b : brokers) + { + b.handleResponse(((AMQFrame) b.getMessages().get(0)).getChannel(), new TestMethod("response")); + } + + assertTrue("Handler did not receive response", handler.isCompleted()); + } + + //group request (failure) + public void testGroupRequest_failure() throws AMQException + { + RecordingBroker a = new RecordingBroker("A", 1); + RecordingBroker b = new RecordingBroker("B", 2); + RecordingBroker c = new RecordingBroker("C", 3); + RecordingBroker[] all = new RecordingBroker[]{a, b, c}; + RecordingBroker[] succeeded = new RecordingBroker[]{a, c}; + + GroupResponseValidator handler = new GroupResponseValidator(new TestMethod("response"), new ArrayList(Arrays.asList(succeeded))); + GroupRequest grpRequest = new GroupRequest(new SimpleBodySendable(new TestMethod("request")), StandardPolicies.SYNCH_POLICY, handler); + + for (Broker broker : all) + { + broker.invoke(grpRequest); + } + grpRequest.finishedSend(); + + for (RecordingBroker broker : succeeded) + { + broker.handleResponse(((AMQFrame) broker.getMessages().get(0)).getChannel(), new TestMethod("response")); + } + b.remove(); + + assertTrue("Handler did not receive response", handler.isCompleted()); + } + + + //simple send (no response) + public void testSend_noResponse() throws AMQException + { + AMQBody[] msgs = new AMQBody[]{ + new TestMethod("A"), + new TestMethod("B"), + new TestMethod("C") + }; + RecordingBroker broker = new RecordingBroker("myhost", 1); + for (AMQBody msg : msgs) + { + broker.send(new SimpleBodySendable(msg), null); + } + List sent = broker.getMessages(); + assertEquals(msgs.length, sent.size()); + for (int i = 0; i < msgs.length; i++) + { + assertTrue(sent.get(i) instanceof AMQFrame); + assertEquals(msgs[i], ((AMQFrame) sent.get(i)).getBodyFrame()); + } + } + + //simple send (no failure) + public void testSend_noFailure() throws AMQException + { + RecordingBroker broker = new RecordingBroker("myhost", 1); + BlockingHandler handler = new BlockingHandler(); + broker.send(new SimpleBodySendable(new TestMethod("A")), handler); + List sent = broker.getMessages(); + assertEquals(1, sent.size()); + assertTrue(sent.get(0) instanceof AMQFrame); + assertEquals(new TestMethod("A"), ((AMQFrame) sent.get(0)).getBodyFrame()); + + broker.handleResponse(((AMQFrame) sent.get(0)).getChannel(), new TestMethod("B")); + + assertEquals(new TestMethod("B"), handler.getResponse()); + } + + //simple send (failure) + public void testSend_failure() throws AMQException + { + RecordingBroker broker = new RecordingBroker("myhost", 1); + BlockingHandler handler = new BlockingHandler(); + broker.send(new SimpleBodySendable(new TestMethod("A")), handler); + List sent = broker.getMessages(); + assertEquals(1, sent.size()); + assertTrue(sent.get(0) instanceof AMQFrame); + assertEquals(new TestMethod("A"), ((AMQFrame) sent.get(0)).getBodyFrame()); + broker.remove(); + assertEquals(null, handler.getResponse()); + assertTrue(handler.isCompleted()); + assertTrue(handler.failed()); + } + + private static class TestMethod extends AMQMethodBody + { + private final Object id; + + TestMethod(Object id) + { + // 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. + super((byte)8, (byte)0); + this.id = id; + } + + protected int getBodySize() + { + return 0; + } + + protected int getClazz() + { + return 1002; + } + + protected int getMethod() + { + return 1003; + } + + protected void writeMethodPayload(ByteBuffer buffer) + { + } + + protected byte getType() + { + return 0; + } + + protected int getSize() + { + return 0; + } + + protected void writePayload(ByteBuffer buffer) + { + } + + protected void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException + { + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + } + + public boolean equals(Object o) + { + return o instanceof TestMethod && id.equals(((TestMethod) o).id); + } + + public int hashCode() + { + return id.hashCode(); + } + + } + + private static class GroupResponseValidator implements GroupResponseHandler + { + private final AMQMethodBody _response; + private final List _members; + private boolean _completed = false; + + GroupResponseValidator(AMQMethodBody response, List members) + { + _response = response; + _members = members; + } + + public void response(List responses, List members) + { + for (AMQMethodBody r : responses) + { + assertEquals(_response, r); + } + assertEquals(_members, members); + _completed = true; + } + + boolean isCompleted() + { + return _completed; + } + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.java new file mode 100644 index 0000000000..830a00f4c2 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/ClusterCapabilityTest.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.cluster; + +import junit.framework.TestCase; +import org.apache.qpid.framing.AMQShortString; + +public class ClusterCapabilityTest extends TestCase +{ + public void testStartWithNull() + { + MemberHandle peer = new SimpleMemberHandle("myhost:9999"); + AMQShortString c = ClusterCapability.add(null, peer); + assertTrue(ClusterCapability.contains(c)); + assertTrue(peer.matches(ClusterCapability.getPeer(c))); + } + + public void testStartWithText() + { + MemberHandle peer = new SimpleMemberHandle("myhost:9999"); + AMQShortString c = ClusterCapability.add(new AMQShortString("existing text"), peer); + assertTrue(ClusterCapability.contains(c)); + assertTrue(peer.matches(ClusterCapability.getPeer(c))); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.java new file mode 100644 index 0000000000..7e58add91e --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/InductionBufferTest.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.cluster; + +import org.apache.mina.common.IoSession; + +import java.util.List; +import java.util.ArrayList; + +import junit.framework.TestCase; + +public class InductionBufferTest extends TestCase +{ + public void test() throws Exception + { + IoSession session1 = new TestSession(); + IoSession session2 = new TestSession(); + IoSession session3 = new TestSession(); + + TestMessageHandler handler = new TestMessageHandler(); + InductionBuffer buffer = new InductionBuffer(handler); + + buffer.receive(session1, "one"); + buffer.receive(session2, "two"); + buffer.receive(session3, "three"); + + buffer.receive(session1, "four"); + buffer.receive(session1, "five"); + buffer.receive(session1, "six"); + + buffer.receive(session3, "seven"); + buffer.receive(session3, "eight"); + + handler.checkEmpty(); + buffer.deliver(); + + handler.check(session1, "one"); + handler.check(session2, "two"); + handler.check(session3, "three"); + + handler.check(session1, "four"); + handler.check(session1, "five"); + handler.check(session1, "six"); + + handler.check(session3, "seven"); + handler.check(session3, "eight"); + handler.checkEmpty(); + + buffer.receive(session1, "nine"); + buffer.receive(session2, "ten"); + buffer.receive(session3, "eleven"); + + handler.check(session1, "nine"); + handler.check(session2, "ten"); + handler.check(session3, "eleven"); + + handler.checkEmpty(); + } + + private static class TestMessageHandler implements InductionBuffer.MessageHandler + { + private final List _sessions = new ArrayList(); + private final List _msgs = new ArrayList(); + + public synchronized void deliver(IoSession session, Object msg) throws Exception + { + _sessions.add(session); + _msgs.add(msg); + } + + void check(IoSession actualSession, Object actualMsg) + { + assertFalse(_sessions.isEmpty()); + assertFalse(_msgs.isEmpty()); + IoSession expectedSession = _sessions.remove(0); + Object expectedMsg = _msgs.remove(0); + assertEquals(expectedSession, actualSession); + assertEquals(expectedMsg, actualMsg); + } + + void checkEmpty() + { + assertTrue(_sessions.isEmpty()); + assertTrue(_msgs.isEmpty()); + } + } +} + diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.java new file mode 100644 index 0000000000..1ec5154a98 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBroker.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; + +import java.util.ArrayList; +import java.util.List; + +class RecordingBroker extends TestBroker +{ + private final List _messages = new ArrayList(); + + RecordingBroker(String host, int port) + { + super(host, port); + } + + public void send(AMQDataBlock data) throws AMQException + { + _messages.add(data); + } + + List getMessages() + { + return _messages; + } + + void clear() + { + _messages.clear(); + } + +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.java new file mode 100644 index 0000000000..d3e972e273 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/RecordingBrokerFactory.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.cluster; + +class RecordingBrokerFactory implements BrokerFactory +{ + public Broker create(MemberHandle handle) + { + return new RecordingBroker(handle.getHost(), handle.getPort()); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.java new file mode 100644 index 0000000000..86cde3cee7 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleClusterTest.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.url.URLSyntaxException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQSession; + +import javax.jms.JMSException; + +import junit.framework.TestCase; + +public class SimpleClusterTest extends TestCase +{ + public void testDeclareExchange() throws AMQException, JMSException, URLSyntaxException + { + AMQConnection con = new AMQConnection("localhost:9000", "guest", "guest", "test", "/test"); + AMQSession session = (AMQSession) con.createSession(false, AMQSession.NO_ACKNOWLEDGE); + System.out.println("Session created"); + session.declareExchange(new AMQShortString("my_exchange"), new AMQShortString("direct"), true); + System.out.println("Exchange declared"); + con.close(); + System.out.println("Connection closed"); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.java new file mode 100644 index 0000000000..8ff8357377 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/SimpleMemberHandleTest.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.cluster; + +import junit.framework.TestCase; + +public class SimpleMemberHandleTest extends TestCase +{ + public void testMatches() + { + assertMatch(new SimpleMemberHandle("localhost", 8888), new SimpleMemberHandle("localhost", 8888)); + assertNoMatch(new SimpleMemberHandle("localhost", 8889), new SimpleMemberHandle("localhost", 8888)); + assertNoMatch(new SimpleMemberHandle("localhost", 8888), new SimpleMemberHandle("localhost2", 8888)); + } + + public void testResolve() + { + assertEquivalent(new SimpleMemberHandle("WGLAIBD8XGR0J:9000"), new SimpleMemberHandle("localhost:9000")); + } + + private void assertEquivalent(MemberHandle a, MemberHandle b) + { + String msg = a + " is not equivalent to " + b; + a = SimpleMemberHandle.resolve(a); + b = SimpleMemberHandle.resolve(b); + msg += "(" + a + " does not match " + b + ")"; + assertTrue(msg, a.matches(b)); + } + + private void assertMatch(MemberHandle a, MemberHandle b) + { + assertTrue(a + " does not match " + b, a.matches(b)); + } + + private void assertNoMatch(MemberHandle a, MemberHandle b) + { + assertFalse(a + " matches " + b, a.matches(b)); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.java new file mode 100644 index 0000000000..d3ccbf0ac6 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBroker.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.cluster; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQDataBlock; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.AMQMethodBody; + +import java.io.IOException; + +class TestBroker extends Broker +{ + TestBroker(String host, int port) + { + super(host, port); + } + + boolean connect() throws IOException, InterruptedException + { + return true; + } + + void connectAsynch(Iterable msgs) + { + replay(msgs); + } + + void replay(Iterable msgs) + { + try + { + for (AMQMethodBody b : msgs) + { + send(new AMQFrame(0, b)); + } + } + catch (AMQException e) + { + throw new RuntimeException(e); + } + } + + Broker connectToCluster() throws IOException, InterruptedException + { + return this; + } + + public void send(AMQDataBlock data) throws AMQException + { + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.java new file mode 100644 index 0000000000..92eaec876a --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestBrokerFactory.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.cluster; + +class TestBrokerFactory implements BrokerFactory +{ + public Broker create(MemberHandle handle) + { + return new TestBroker(handle.getHost(), handle.getPort()); + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.java new file mode 100644 index 0000000000..c529c83cc0 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestReplayManager.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.cluster; + +import org.apache.qpid.framing.AMQMethodBody; +import org.apache.qpid.server.cluster.replay.ReplayManager; + +import java.util.ArrayList; +import java.util.List; + +class TestReplayManager implements ReplayManager +{ + private final List _msgs; + + TestReplayManager() + { + this(new ArrayList()); + } + + TestReplayManager(List msgs) + { + _msgs = msgs; + } + + public List replay(boolean isLeader) + { + return _msgs; + } +} diff --git a/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.java new file mode 100644 index 0000000000..86ec808924 --- /dev/null +++ b/Final2/java/cluster/src/test/java/org/apache/qpid/server/cluster/TestSession.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.server.cluster; + +import org.apache.mina.common.*; + +import java.net.SocketAddress; +import java.util.Set; + +class TestSession implements IoSession +{ + public IoService getService() + { + return null; //TODO + } + + public IoServiceConfig getServiceConfig() + { + return null; //TODO + } + + public IoHandler getHandler() + { + return null; //TODO + } + + public IoSessionConfig getConfig() + { + return null; //TODO + } + + public IoFilterChain getFilterChain() + { + return null; //TODO + } + + public WriteFuture write(Object message) + { + return null; //TODO + } + + public CloseFuture close() + { + return null; //TODO + } + + public Object getAttachment() + { + return null; //TODO + } + + public Object setAttachment(Object attachment) + { + return null; //TODO + } + + public Object getAttribute(String key) + { + return null; //TODO + } + + public Object setAttribute(String key, Object value) + { + return null; //TODO + } + + public Object setAttribute(String key) + { + return null; //TODO + } + + public Object removeAttribute(String key) + { + return null; //TODO + } + + public boolean containsAttribute(String key) + { + return false; //TODO + } + + public Set getAttributeKeys() + { + return null; //TODO + } + + public TransportType getTransportType() + { + return null; //TODO + } + + public boolean isConnected() + { + return false; //TODO + } + + public boolean isClosing() + { + return false; //TODO + } + + public CloseFuture getCloseFuture() + { + return null; //TODO + } + + public SocketAddress getRemoteAddress() + { + return null; //TODO + } + + public SocketAddress getLocalAddress() + { + return null; //TODO + } + + public SocketAddress getServiceAddress() + { + return null; //TODO + } + + public int getIdleTime(IdleStatus status) + { + return 0; //TODO + } + + public long getIdleTimeInMillis(IdleStatus status) + { + return 0; //TODO + } + + public void setIdleTime(IdleStatus status, int idleTime) + { + //TODO + } + + public int getWriteTimeout() + { + return 0; //TODO + } + + public long getWriteTimeoutInMillis() + { + return 0; //TODO + } + + public void setWriteTimeout(int writeTimeout) + { + //TODO + } + + public TrafficMask getTrafficMask() + { + return null; //TODO + } + + public void setTrafficMask(TrafficMask trafficMask) + { + //TODO + } + + public void suspendRead() + { + //TODO + } + + public void suspendWrite() + { + //TODO + } + + public void resumeRead() + { + //TODO + } + + public void resumeWrite() + { + //TODO + } + + public long getReadBytes() + { + return 0; //TODO + } + + public long getWrittenBytes() + { + return 0; //TODO + } + + public long getReadMessages() + { + return 0; + } + + public long getWrittenMessages() + { + return 0; + } + + public long getWrittenWriteRequests() + { + return 0; //TODO + } + + public int getScheduledWriteRequests() + { + return 0; //TODO + } + + public int getScheduledWriteBytes() + { + return 0; //TODO + } + + public long getCreationTime() + { + return 0; //TODO + } + + public long getLastIoTime() + { + return 0; //TODO + } + + public long getLastReadTime() + { + return 0; //TODO + } + + public long getLastWriteTime() + { + return 0; //TODO + } + + public boolean isIdle(IdleStatus status) + { + return false; //TODO + } + + public int getIdleCount(IdleStatus status) + { + return 0; //TODO + } + + public long getLastIdleTime(IdleStatus status) + { + return 0; //TODO + } +} diff --git a/Final2/java/common/bin/qpid-run b/Final2/java/common/bin/qpid-run new file mode 100644 index 0000000000..c9e37b21a1 --- /dev/null +++ b/Final2/java/common/bin/qpid-run @@ -0,0 +1,238 @@ +#!/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 +} + +if [ -z $AMQJ_LOGGING_LEVEL ]; then + export AMQJ_LOGGING_LEVEL=info +fi + +if [ -z "$QPID_HOME" ]; then + die "QPID_HOME must be set" +fi + +if [ -z "$QPID_WORK" ]; then + echo Setting QPID_WORK to $HOME as default + QPID_WORK=$HOME +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 + echo Using pid in qpid log name prefix + LOG_PREFIX=" -Dlogprefix=$$" + else + echo 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 + echo Using pid in qpid log name suffix + LOG_SUFFIX=" -Dlogsuffix=$$" + else + echo Using qpig logsuffix property + LOG_SUFFIX=" -Dlogsuffix=$QPID_LOG_SUFFIX" + fi + SYSTEM_PROPS="${SYSTEM_PROPS} ${LOG_SUFFIX}" +fi + +echo 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: