From 38cde902ffe68eac8ffb0884bcc9c7bfa98c02ac Mon Sep 17 00:00:00 2001 From: Rajith Muditha Attapattu Date: Fri, 19 Dec 2008 19:34:45 +0000 Subject: Tagging RC5 for M4 release git-svn-id: https://svn.apache.org/repos/asf/qpid/tags/M4@728121 13f79535-47bb-0310-9956-ffa450edef68 --- RC5/java/common/src/main/java/common.bnd | 6 + RC5/java/common/src/main/java/log4j.properties | 31 + .../mina/common/FixedSizeByteBufferAllocator.java | 467 ++++++++ .../mina/common/support/DefaultIoFuture.java | 227 ++++ .../common/support/IoServiceListenerSupport.java | 351 ++++++ .../mina/filter/WriteBufferFullExeception.java | 48 + .../mina/filter/WriteBufferLimitFilterBuilder.java | 272 +++++ .../filter/codec/OurCumulativeProtocolDecoder.java | 197 ++++ .../mina/filter/codec/QpidProtocolCodecFilter.java | 440 ++++++++ .../socket/nio/MultiThreadSocketAcceptor.java | 547 +++++++++ .../socket/nio/MultiThreadSocketConnector.java | 486 ++++++++ .../socket/nio/MultiThreadSocketFilterChain.java | 67 ++ .../socket/nio/MultiThreadSocketIoProcessor.java | 1026 +++++++++++++++++ .../nio/MultiThreadSocketSessionConfigImpl.java | 240 ++++ .../socket/nio/MultiThreadSocketSessionImpl.java | 488 ++++++++ .../mina/transport/vmpipe/QpidVmPipeConnector.java | 151 +++ .../org/apache/qpid/AMQChannelClosedException.java | 41 + .../java/org/apache/qpid/AMQChannelException.java | 59 + .../apache/qpid/AMQConnectionClosedException.java | 44 + .../org/apache/qpid/AMQConnectionException.java | 70 ++ .../apache/qpid/AMQConnectionFailureException.java | 65 ++ .../org/apache/qpid/AMQDisconnectedException.java | 39 + .../main/java/org/apache/qpid/AMQException.java | 122 ++ .../apache/qpid/AMQInvalidArgumentException.java | 45 + .../apache/qpid/AMQInvalidRoutingKeyException.java | 39 + .../org/apache/qpid/AMQPInvalidClassException.java | 39 + .../java/org/apache/qpid/AMQProtocolException.java | 38 + .../java/org/apache/qpid/AMQTimeoutException.java | 39 + .../org/apache/qpid/AMQUndeliveredException.java | 54 + .../org/apache/qpid/AMQUnknownExchangeType.java | 43 + .../apache/qpid/AMQUnresolvedAddressException.java | 50 + .../main/java/org/apache/qpid/BrokerDetails.java | 138 +++ .../java/org/apache/qpid/BrokerDetailsImpl.java | 264 +++++ .../main/java/org/apache/qpid/ConsoleOutput.java | 54 + .../src/main/java/org/apache/qpid/ErrorCode.java | 123 ++ .../src/main/java/org/apache/qpid/QpidConfig.java | 111 ++ .../main/java/org/apache/qpid/QpidException.java | 58 + .../main/java/org/apache/qpid/SecurityHelper.java | 71 ++ .../main/java/org/apache/qpid/SerialException.java | 40 + .../src/main/java/org/apache/qpid/ToyBroker.java | 188 ++++ .../src/main/java/org/apache/qpid/ToyClient.java | 106 ++ .../src/main/java/org/apache/qpid/ToyExchange.java | 154 +++ .../src/main/java/org/apache/qpid/api/Message.java | 126 +++ .../org/apache/qpid/codec/AMQCodecFactory.java | 77 ++ .../java/org/apache/qpid/codec/AMQDecoder.java | 271 +++++ .../java/org/apache/qpid/codec/AMQEncoder.java | 66 ++ .../org/apache/qpid/common/AMQPFilterTypes.java | 61 + .../org/apache/qpid/common/ClientProperties.java | 52 + .../org/apache/qpid/common/QpidProperties.java | 190 ++++ .../org/apache/qpid/configuration/Configured.java | 44 + .../qpid/configuration/PropertyException.java | 43 + .../apache/qpid/configuration/PropertyUtils.java | 164 +++ .../src/main/java/org/apache/qpid/dtx/XidImpl.java | 250 +++++ .../org/apache/qpid/exchange/ExchangeDefaults.java | 65 ++ .../main/java/org/apache/qpid/framing/AMQBody.java | 40 + .../java/org/apache/qpid/framing/AMQDataBlock.java | 63 ++ .../apache/qpid/framing/AMQDataBlockDecoder.java | 120 ++ .../apache/qpid/framing/AMQDataBlockEncoder.java | 61 + .../java/org/apache/qpid/framing/AMQFrame.java | 125 +++ .../qpid/framing/AMQFrameDecodingException.java | 47 + .../org/apache/qpid/framing/AMQMethodBody.java | 83 ++ .../apache/qpid/framing/AMQMethodBodyFactory.java | 45 + .../org/apache/qpid/framing/AMQMethodBodyImpl.java | 96 ++ .../qpid/framing/AMQMethodBodyInstanceFactory.java | 30 + .../org/apache/qpid/framing/AMQMethodFactory.java | 90 ++ .../qpid/framing/AMQProtocolClassException.java | 39 + .../qpid/framing/AMQProtocolHeaderException.java | 41 + .../qpid/framing/AMQProtocolInstanceException.java | 39 + .../qpid/framing/AMQProtocolVersionException.java | 39 + .../org/apache/qpid/framing/AMQShortString.java | 776 +++++++++++++ .../qpid/framing/AMQShortStringTokenizer.java | 31 + .../main/java/org/apache/qpid/framing/AMQType.java | 795 +++++++++++++ .../java/org/apache/qpid/framing/AMQTypeMap.java | 48 + .../org/apache/qpid/framing/AMQTypedValue.java | 116 ++ .../qpid/framing/BasicContentHeaderProperties.java | 834 ++++++++++++++ .../java/org/apache/qpid/framing/BodyFactory.java | 31 + .../framing/CommonContentHeaderProperties.java | 81 ++ .../apache/qpid/framing/CompositeAMQDataBlock.java | 78 ++ .../main/java/org/apache/qpid/framing/Content.java | 26 + .../java/org/apache/qpid/framing/ContentBody.java | 121 ++ .../apache/qpid/framing/ContentBodyFactory.java | 48 + .../org/apache/qpid/framing/ContentHeaderBody.java | 131 +++ .../qpid/framing/ContentHeaderBodyFactory.java | 50 + .../qpid/framing/ContentHeaderProperties.java | 60 + .../framing/ContentHeaderPropertiesFactory.java | 59 + .../org/apache/qpid/framing/DeferredDataBlock.java | 50 + .../apache/qpid/framing/EncodableAMQDataBlock.java | 35 + .../org/apache/qpid/framing/EncodingUtils.java | 1033 +++++++++++++++++ .../java/org/apache/qpid/framing/FieldTable.java | 1187 ++++++++++++++++++++ .../org/apache/qpid/framing/FieldTableFactory.java | 38 + .../org/apache/qpid/framing/HeartbeatBody.java | 79 ++ .../apache/qpid/framing/HeartbeatBodyFactory.java | 31 + .../apache/qpid/framing/ProtocolInitiation.java | 195 ++++ .../qpid/framing/SmallCompositeAMQDataBlock.java | 98 ++ .../qpid/framing/VersionSpecificRegistry.java | 198 ++++ .../abstraction/AbstractMethodConverter.java | 47 + .../qpid/framing/abstraction/ContentChunk.java | 32 + .../framing/abstraction/MessagePublishInfo.java | 38 + .../abstraction/MessagePublishInfoConverter.java | 32 + .../ProtocolVersionMethodConverter.java | 32 + .../qpid/framing/amqp_0_9/AMQMethodBody_0_9.java | 209 ++++ .../qpid/framing/amqp_0_9/MethodConverter_0_9.java | 172 +++ .../qpid/framing/amqp_8_0/AMQMethodBody_8_0.java | 209 ++++ .../qpid/framing/amqp_8_0/MethodConverter_8_0.java | 151 +++ .../src/main/java/org/apache/qpid/pool/Event.java | 155 +++ .../src/main/java/org/apache/qpid/pool/Job.java | 192 ++++ .../java/org/apache/qpid/pool/PoolingFilter.java | 491 ++++++++ .../org/apache/qpid/pool/ReadWriteJobQueue.java | 432 +++++++ .../org/apache/qpid/pool/ReadWriteRunnable.java | 27 + .../org/apache/qpid/pool/ReadWriteThreadModel.java | 102 ++ .../pool/ReferenceCountingExecutorService.java | 172 +++ .../java/org/apache/qpid/protocol/AMQConstant.java | 236 ++++ .../org/apache/qpid/protocol/AMQMethodEvent.java | 95 ++ .../apache/qpid/protocol/AMQMethodListener.java | 70 ++ .../apache/qpid/protocol/AMQProtocolWriter.java | 43 + .../protocol/AMQVersionAwareProtocolSession.java | 64 ++ .../apache/qpid/protocol/ProtocolVersionAware.java | 53 + .../apache/qpid/security/AMQPCallbackHandler.java | 28 + .../qpid/security/CallbackHandlerRegistry.java | 94 ++ .../apache/qpid/security/DynamicSaslRegistrar.java | 80 ++ .../java/org/apache/qpid/security/JCAProvider.java | 44 + .../security/UsernamePasswordCallbackHandler.java | 60 + .../qpid/security/amqplain/AmqPlainSaslClient.java | 105 ++ .../amqplain/AmqPlainSaslClientFactory.java | 62 + .../org/apache/qpid/ssl/SSLContextFactory.java | 195 ++++ .../java/org/apache/qpid/transport/Binary.java | 145 +++ .../java/org/apache/qpid/transport/Binding.java | 36 + .../org/apache/qpid/transport/ClientDelegate.java | 138 +++ .../java/org/apache/qpid/transport/Connection.java | 505 +++++++++ .../apache/qpid/transport/ConnectionDelegate.java | 98 ++ .../apache/qpid/transport/ConnectionException.java | 70 ++ .../apache/qpid/transport/ConnectionListener.java | 38 + .../main/java/org/apache/qpid/transport/Echo.java | 71 ++ .../main/java/org/apache/qpid/transport/Field.java | 83 ++ .../java/org/apache/qpid/transport/Future.java | 37 + .../java/org/apache/qpid/transport/Header.java | 92 ++ .../java/org/apache/qpid/transport/Method.java | 180 +++ .../apache/qpid/transport/ProtocolDelegate.java | 40 + .../org/apache/qpid/transport/ProtocolError.java | 83 ++ .../org/apache/qpid/transport/ProtocolEvent.java | 40 + .../org/apache/qpid/transport/ProtocolHeader.java | 116 ++ .../qpid/transport/ProtocolVersionException.java | 62 + .../main/java/org/apache/qpid/transport/Range.java | 125 +++ .../java/org/apache/qpid/transport/RangeSet.java | 147 +++ .../java/org/apache/qpid/transport/Receiver.java | 38 + .../java/org/apache/qpid/transport/Result.java | 30 + .../java/org/apache/qpid/transport/Sender.java | 38 + .../org/apache/qpid/transport/SenderException.java | 52 + .../org/apache/qpid/transport/ServerDelegate.java | 154 +++ .../java/org/apache/qpid/transport/Session.java | 779 +++++++++++++ .../qpid/transport/SessionClosedException.java | 49 + .../org/apache/qpid/transport/SessionDelegate.java | 147 +++ .../apache/qpid/transport/SessionException.java | 61 + .../org/apache/qpid/transport/SessionListener.java | 40 + .../main/java/org/apache/qpid/transport/Sink.java | 136 +++ .../java/org/apache/qpid/transport/Struct.java | 142 +++ .../apache/qpid/transport/TransportException.java | 51 + .../qpid/transport/codec/AbstractDecoder.java | 470 ++++++++ .../qpid/transport/codec/AbstractEncoder.java | 620 ++++++++++ .../org/apache/qpid/transport/codec/BBDecoder.java | 144 +++ .../org/apache/qpid/transport/codec/BBEncoder.java | 323 ++++++ .../org/apache/qpid/transport/codec/Decoder.java | 283 +++++ .../org/apache/qpid/transport/codec/Encodable.java | 44 + .../org/apache/qpid/transport/codec/Encoder.java | 282 +++++ .../apache/qpid/transport/network/Assembler.java | 226 ++++ .../qpid/transport/network/ConnectionBinding.java | 83 ++ .../qpid/transport/network/Disassembler.java | 239 ++++ .../org/apache/qpid/transport/network/Frame.java | 151 +++ .../qpid/transport/network/InputHandler.java | 204 ++++ .../qpid/transport/network/NetworkDelegate.java | 42 + .../qpid/transport/network/NetworkEvent.java | 34 + .../transport/network/io/InputHandler_0_9.java | 130 +++ .../qpid/transport/network/io/IoAcceptor.java | 92 ++ .../qpid/transport/network/io/IoReceiver.java | 136 +++ .../apache/qpid/transport/network/io/IoSender.java | 286 +++++ .../qpid/transport/network/io/IoTransport.java | 229 ++++ .../qpid/transport/network/mina/MinaHandler.java | 274 +++++ .../qpid/transport/network/mina/MinaSender.java | 81 ++ .../qpid/transport/network/nio/NioHandler.java | 140 +++ .../qpid/transport/network/nio/NioSender.java | 121 ++ .../qpid/transport/network/ssl/SSLReceiver.java | 179 +++ .../qpid/transport/network/ssl/SSLSender.java | 197 ++++ .../org/apache/qpid/transport/util/Functions.java | 97 ++ .../org/apache/qpid/transport/util/Logger.java | 130 +++ .../apache/qpid/transport/util/SliceIterator.java | 59 + .../org/apache/qpid/transport/util/Waiter.java | 63 ++ .../java/org/apache/qpid/url/AMQBindingURL.java | 236 ++++ .../main/java/org/apache/qpid/url/BindingURL.java | 59 + .../java/org/apache/qpid/url/BindingURLParser.java | 466 ++++++++ .../main/java/org/apache/qpid/url/URLHelper.java | 173 +++ .../org/apache/qpid/url/URLSyntaxException.java | 97 ++ .../org/apache/qpid/util/CommandLineParser.java | 689 ++++++++++++ .../ConcurrentLinkedMessageQueueAtomicSize.java | 258 +++++ .../qpid/util/ConcurrentLinkedQueueAtomicSize.java | 70 ++ .../qpid/util/ConcurrentLinkedQueueNoSize.java | 38 + .../main/java/org/apache/qpid/util/FileUtils.java | 316 ++++++ .../java/org/apache/qpid/util/MessageQueue.java | 43 + .../java/org/apache/qpid/util/NameUUIDGen.java | 59 + .../org/apache/qpid/util/PrettyPrintingUtils.java | 75 ++ .../java/org/apache/qpid/util/PropertiesUtils.java | 200 ++++ .../java/org/apache/qpid/util/RandomUUIDGen.java | 39 + .../src/main/java/org/apache/qpid/util/Serial.java | 108 ++ .../main/java/org/apache/qpid/util/Strings.java | 94 ++ .../main/java/org/apache/qpid/util/UUIDGen.java | 36 + .../src/main/java/org/apache/qpid/util/UUIDs.java | 59 + .../util/concurrent/AlreadyUnblockedException.java | 34 + .../qpid/util/concurrent/BatchSynchQueue.java | 122 ++ .../qpid/util/concurrent/BatchSynchQueueBase.java | 834 ++++++++++++++ .../apache/qpid/util/concurrent/BooleanLatch.java | 128 +++ .../org/apache/qpid/util/concurrent/Capacity.java | 35 + .../org/apache/qpid/util/concurrent/Condition.java | 50 + .../apache/qpid/util/concurrent/SynchBuffer.java | 50 + .../qpid/util/concurrent/SynchException.java | 52 + .../apache/qpid/util/concurrent/SynchQueue.java | 48 + .../apache/qpid/util/concurrent/SynchRecord.java | 74 ++ .../org/apache/qpid/util/concurrent/SynchRef.java | 51 + .../main/resources/org/apache/qpid/ssl/qpid.cert | Bin 0 -> 756 bytes .../apache/mina/SocketIOTest/IOWriterClient.java | 396 +++++++ .../apache/mina/SocketIOTest/IOWriterServer.java | 157 +++ .../java/org/apache/qpid/AMQExceptionTest.java | 106 ++ .../apache/qpid/framing/AMQShortStringTest.java | 109 ++ .../framing/BasicContentHeaderPropertiesTest.java | 188 ++++ .../qpid/framing/PropertyFieldTableTest.java | 960 ++++++++++++++++ .../org/apache/qpid/pool/PoolingFilterTest.java | 111 ++ .../java/org/apache/qpid/session/TestSession.java | 277 +++++ .../org/apache/qpid/transport/ConnectionTest.java | 365 ++++++ .../java/org/apache/qpid/transport/GenTest.java | 44 + .../org/apache/qpid/transport/RangeSetTest.java | 238 ++++ .../apache/qpid/util/CommandLineParserTest.java | 554 +++++++++ .../java/org/apache/qpid/util/FileUtilsTest.java | 522 +++++++++ .../test/java/org/apache/qpid/util/SerialTest.java | 82 ++ 231 files changed, 37340 insertions(+) create mode 100755 RC5/java/common/src/main/java/common.bnd create mode 100644 RC5/java/common/src/main/java/log4j.properties create mode 100644 RC5/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java create mode 100644 RC5/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQChannelException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/BrokerDetails.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ErrorCode.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/QpidConfig.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/QpidException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/SecurityHelper.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/SerialException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ToyBroker.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ToyClient.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ToyExchange.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/api/Message.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/configuration/Configured.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQType.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/Content.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/Event.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/Job.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodEvent.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQMethodListener.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQProtocolWriter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQVersionAwareProtocolSession.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/protocol/ProtocolVersionAware.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/AMQPCallbackHandler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/CallbackHandlerRegistry.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/DynamicSaslRegistrar.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/JCAProvider.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/UsernamePasswordCallbackHandler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClient.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/security/amqplain/AmqPlainSaslClientFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Binary.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Binding.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ClientDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Connection.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ConnectionDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ConnectionException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ConnectionListener.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Echo.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Field.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Future.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Header.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Method.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ProtocolDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ProtocolError.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ProtocolEvent.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ProtocolHeader.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ProtocolVersionException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Range.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/RangeSet.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Receiver.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Result.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Sender.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/SenderException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/ServerDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Session.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/SessionClosedException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/SessionDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/SessionException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/SessionListener.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Sink.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/Struct.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/TransportException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractDecoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/BBDecoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/BBEncoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/Decoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/Encodable.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/codec/Encoder.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/Assembler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/ConnectionBinding.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/Disassembler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/Frame.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/InputHandler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/NetworkDelegate.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/NetworkEvent.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/io/InputHandler_0_9.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/io/IoAcceptor.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/io/IoReceiver.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/io/IoSender.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/io/IoTransport.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaHandler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/mina/MinaSender.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioHandler.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/nio/NioSender.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLReceiver.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/network/ssl/SSLSender.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/util/Functions.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/util/Logger.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/util/SliceIterator.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/transport/util/Waiter.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/url/AMQBindingURL.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/url/BindingURL.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/url/BindingURLParser.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/url/URLHelper.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/url/URLSyntaxException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/CommandLineParser.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedMessageQueueAtomicSize.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueAtomicSize.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/ConcurrentLinkedQueueNoSize.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/FileUtils.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/MessageQueue.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/NameUUIDGen.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/PrettyPrintingUtils.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/PropertiesUtils.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/RandomUUIDGen.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/Serial.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/Strings.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/UUIDGen.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/UUIDs.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/AlreadyUnblockedException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueue.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/BatchSynchQueueBase.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/BooleanLatch.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/Capacity.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/Condition.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchBuffer.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchException.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchQueue.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRecord.java create mode 100644 RC5/java/common/src/main/java/org/apache/qpid/util/concurrent/SynchRef.java create mode 100644 RC5/java/common/src/main/resources/org/apache/qpid/ssl/qpid.cert create mode 100644 RC5/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterClient.java create mode 100644 RC5/java/common/src/test/java/org/apache/mina/SocketIOTest/IOWriterServer.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/AMQExceptionTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/framing/AMQShortStringTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/framing/BasicContentHeaderPropertiesTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/framing/PropertyFieldTableTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/pool/PoolingFilterTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/session/TestSession.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/transport/ConnectionTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/transport/GenTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/transport/RangeSetTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/util/CommandLineParserTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/util/FileUtilsTest.java create mode 100644 RC5/java/common/src/test/java/org/apache/qpid/util/SerialTest.java (limited to 'RC5/java/common/src') diff --git a/RC5/java/common/src/main/java/common.bnd b/RC5/java/common/src/main/java/common.bnd new file mode 100755 index 0000000000..a534451390 --- /dev/null +++ b/RC5/java/common/src/main/java/common.bnd @@ -0,0 +1,6 @@ +ver: M4 + +Bundle-SymbolicName: qpid_common +Bundle-Version: ${ver} +Export-Package: *;version=${ver} +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/RC5/java/common/src/main/java/log4j.properties b/RC5/java/common/src/main/java/log4j.properties new file mode 100644 index 0000000000..44f89dc805 --- /dev/null +++ b/RC5/java/common/src/main/java/log4j.properties @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +log4j.rootLogger=${root.logging.level} + + +log4j.logger.qpid.protocol=${amqj.protocol.logging.level}, console +log4j.additivity.qpid.protocol=false +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/RC5/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java b/RC5/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java new file mode 100644 index 0000000000..0c311b6645 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java @@ -0,0 +1,467 @@ +package org.apache.mina.common; + +import org.apache.mina.common.ByteBuffer; + +import java.nio.*; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 FixedSizeByteBufferAllocator implements ByteBufferAllocator +{ + + + private static final int MINIMUM_CAPACITY = 1; + + public FixedSizeByteBufferAllocator () + { + } + + public ByteBuffer allocate( int capacity, boolean direct ) + { + java.nio.ByteBuffer nioBuffer; + if( direct ) + { + nioBuffer = java.nio.ByteBuffer.allocateDirect( capacity ); + } + else + { + nioBuffer = java.nio.ByteBuffer.allocate( capacity ); + } + return new FixedSizeByteBuffer( nioBuffer ); + } + + public ByteBuffer wrap( java.nio.ByteBuffer nioBuffer ) + { + return new FixedSizeByteBuffer( nioBuffer ); + } + + public void dispose() + { + } + + + + private static final class FixedSizeByteBuffer extends ByteBuffer + { + private java.nio.ByteBuffer buf; + private int mark = -1; + + + protected FixedSizeByteBuffer( java.nio.ByteBuffer buf ) + { + this.buf = buf; + buf.order( ByteOrder.BIG_ENDIAN ); + } + + public synchronized void acquire() + { + } + + public void release() + { + } + + public java.nio.ByteBuffer buf() + { + return buf; + } + + public boolean isPooled() + { + return false; + } + + public void setPooled( boolean pooled ) + { + } + + public ByteBuffer duplicate() { + return new FixedSizeByteBuffer( this.buf.duplicate() ); + } + + public ByteBuffer slice() { + return new FixedSizeByteBuffer( this.buf.slice() ); + } + + public ByteBuffer asReadOnlyBuffer() { + return new FixedSizeByteBuffer( this.buf.asReadOnlyBuffer() ); + } + + public byte[] array() + { + return buf.array(); + } + + public int arrayOffset() + { + return buf.arrayOffset(); + } + + public boolean isDirect() + { + return buf.isDirect(); + } + + public boolean isReadOnly() + { + return buf.isReadOnly(); + } + + public int capacity() + { + return buf.capacity(); + } + + public ByteBuffer capacity( int newCapacity ) + { + if( newCapacity > capacity() ) + { + throw new IllegalArgumentException(); + } + + return this; + } + + + + public boolean isAutoExpand() + { + return false; + } + + public ByteBuffer setAutoExpand( boolean autoExpand ) + { + if(autoExpand) throw new IllegalArgumentException(); + else return this; + } + + public ByteBuffer expand( int pos, int expectedRemaining ) + { + int end = pos + expectedRemaining; + if( end > capacity() ) + { + // The buffer needs expansion. + capacity( end ); + } + + if( end > limit() ) + { + // We call limit() directly to prevent StackOverflowError + buf.limit( end ); + } + return this; + } + + public int position() + { + return buf.position(); + } + + public ByteBuffer position( int newPosition ) + { + + buf.position( newPosition ); + if( mark > newPosition ) + { + mark = -1; + } + return this; + } + + public int limit() + { + return buf.limit(); + } + + public ByteBuffer limit( int newLimit ) + { + buf.limit( newLimit ); + if( mark > newLimit ) + { + mark = -1; + } + return this; + } + + public ByteBuffer mark() + { + buf.mark(); + mark = position(); + return this; + } + + public int markValue() + { + return mark; + } + + public ByteBuffer reset() + { + buf.reset(); + return this; + } + + public ByteBuffer clear() + { + buf.clear(); + mark = -1; + return this; + } + + public ByteBuffer flip() + { + buf.flip(); + mark = -1; + return this; + } + + public ByteBuffer rewind() + { + buf.rewind(); + mark = -1; + return this; + } + + public byte get() + { + return buf.get(); + } + + public ByteBuffer put( byte b ) + { + buf.put( b ); + return this; + } + + public byte get( int index ) + { + return buf.get( index ); + } + + public ByteBuffer put( int index, byte b ) + { + buf.put( index, b ); + return this; + } + + public ByteBuffer get( byte[] dst, int offset, int length ) + { + buf.get( dst, offset, length ); + return this; + } + + public ByteBuffer put( java.nio.ByteBuffer src ) + { + buf.put( src ); + return this; + } + + public ByteBuffer put( byte[] src, int offset, int length ) + { + buf.put( src, offset, length ); + return this; + } + + public ByteBuffer compact() + { + buf.compact(); + mark = -1; + return this; + } + + public ByteOrder order() + { + return buf.order(); + } + + public ByteBuffer order( ByteOrder bo ) + { + buf.order( bo ); + return this; + } + + public char getChar() + { + return buf.getChar(); + } + + public ByteBuffer putChar( char value ) + { + buf.putChar( value ); + return this; + } + + public char getChar( int index ) + { + return buf.getChar( index ); + } + + public ByteBuffer putChar( int index, char value ) + { + buf.putChar( index, value ); + return this; + } + + public CharBuffer asCharBuffer() + { + return buf.asCharBuffer(); + } + + public short getShort() + { + return buf.getShort(); + } + + public ByteBuffer putShort( short value ) + { + buf.putShort( value ); + return this; + } + + public short getShort( int index ) + { + return buf.getShort( index ); + } + + public ByteBuffer putShort( int index, short value ) + { + buf.putShort( index, value ); + return this; + } + + public ShortBuffer asShortBuffer() + { + return buf.asShortBuffer(); + } + + public int getInt() + { + return buf.getInt(); + } + + public ByteBuffer putInt( int value ) + { + buf.putInt( value ); + return this; + } + + public int getInt( int index ) + { + return buf.getInt( index ); + } + + public ByteBuffer putInt( int index, int value ) + { + buf.putInt( index, value ); + return this; + } + + public IntBuffer asIntBuffer() + { + return buf.asIntBuffer(); + } + + public long getLong() + { + return buf.getLong(); + } + + public ByteBuffer putLong( long value ) + { + buf.putLong( value ); + return this; + } + + public long getLong( int index ) + { + return buf.getLong( index ); + } + + public ByteBuffer putLong( int index, long value ) + { + buf.putLong( index, value ); + return this; + } + + public LongBuffer asLongBuffer() + { + return buf.asLongBuffer(); + } + + public float getFloat() + { + return buf.getFloat(); + } + + public ByteBuffer putFloat( float value ) + { + buf.putFloat( value ); + return this; + } + + public float getFloat( int index ) + { + return buf.getFloat( index ); + } + + public ByteBuffer putFloat( int index, float value ) + { + buf.putFloat( index, value ); + return this; + } + + public FloatBuffer asFloatBuffer() + { + return buf.asFloatBuffer(); + } + + public double getDouble() + { + return buf.getDouble(); + } + + public ByteBuffer putDouble( double value ) + { + buf.putDouble( value ); + return this; + } + + public double getDouble( int index ) + { + return buf.getDouble( index ); + } + + public ByteBuffer putDouble( int index, double value ) + { + buf.putDouble( index, value ); + return this; + } + + public DoubleBuffer asDoubleBuffer() + { + return buf.asDoubleBuffer(); + } + + + } + + +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java b/RC5/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.java new file mode 100644 index 0000000000..4fd28c4eb5 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/common/support/DefaultIoFuture.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.mina.common.support; + +import org.apache.mina.common.IoFuture; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.IoFutureListener; + +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * A default implementation of {@link org.apache.mina.common.IoFuture}. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + */ +public class DefaultIoFuture implements IoFuture +{ + private final IoSession session; + private final Object lock; + private List listeners; + private Object result; + private boolean ready; + + + /** + * Creates a new instance. + * + * @param session an {@link IoSession} which is associated with this future + */ + public DefaultIoFuture( IoSession session ) + { + this.session = session; + this.lock = this; + } + + /** + * Creates a new instance which uses the specified object as a lock. + */ + public DefaultIoFuture( IoSession session, Object lock ) + { + if( lock == null ) + { + throw new NullPointerException( "lock" ); + } + this.session = session; + this.lock = lock; + } + + public IoSession getSession() + { + return session; + } + + public Object getLock() + { + return lock; + } + + public void join() + { + synchronized( lock ) + { + while( !ready ) + { + try + { + lock.wait(); + } + catch( InterruptedException e ) + { + } + } + } + } + + public boolean join( long timeoutInMillis ) + { + long startTime = ( timeoutInMillis <= 0 ) ? 0 : System + .currentTimeMillis(); + long waitTime = timeoutInMillis; + + synchronized( lock ) + { + if( ready ) + { + return ready; + } + else if( waitTime <= 0 ) + { + return ready; + } + + for( ;; ) + { + try + { + lock.wait( waitTime ); + } + catch( InterruptedException e ) + { + } + + if( ready ) + return true; + else + { + waitTime = timeoutInMillis - ( System.currentTimeMillis() - startTime ); + if( waitTime <= 0 ) + { + return ready; + } + } + } + } + } + + public boolean isReady() + { + synchronized( lock ) + { + return ready; + } + } + + /** + * Sets the result of the asynchronous operation, and mark it as finished. + */ + protected void setValue( Object newValue ) + { + synchronized( lock ) + { + // Allow only once. + if( ready ) + { + return; + } + + result = newValue; + ready = true; + lock.notifyAll(); + + notifyListeners(); + } + } + + /** + * Returns the result of the asynchronous operation. + */ + protected Object getValue() + { + synchronized( lock ) + { + return result; + } + } + + public void addListener( IoFutureListener listener ) + { + if( listener == null ) + { + throw new NullPointerException( "listener" ); + } + + synchronized( lock ) + { + if(listeners == null) + { + listeners = new ArrayList(); + } + listeners.add( listener ); + if( ready ) + { + listener.operationComplete( this ); + } + } + } + + public void removeListener( IoFutureListener listener ) + { + if( listener == null ) + { + throw new NullPointerException( "listener" ); + } + + synchronized( lock ) + { + listeners.remove( listener ); + } + } + + private void notifyListeners() + { + synchronized( lock ) + { + + if(listeners != null) + { + + for( Iterator i = listeners.iterator(); i.hasNext(); ) { + ( ( IoFutureListener ) i.next() ).operationComplete( this ); + } + } + } + } +} + + + diff --git a/RC5/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java b/RC5/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java new file mode 100644 index 0000000000..5723ffbaa9 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/common/support/IoServiceListenerSupport.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 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.common.support; + +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collections; +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.CountDownLatch; + +import org.apache.mina.common.IoAcceptorConfig; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoFuture; +import org.apache.mina.common.IoFutureListener; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoService; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.IoServiceListener; +import org.apache.mina.common.IoSession; +import org.apache.mina.util.IdentityHashSet; + +/** + * A helper which provides addition and removal of {@link IoServiceListener}s and firing + * events. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 446526 $, $Date: 2006-09-15 01:44:11 -0400 (Fri, 15 Sep 2006) $ + */ +public class IoServiceListenerSupport +{ + /** + * A list of {@link IoServiceListener}s. + */ + private final List listeners = new ArrayList(); + + /** + * Tracks managed serviceAddresses. + */ + private final Set managedServiceAddresses = new HashSet(); + + /** + * Tracks managed sesssions with serviceAddress as a key. + */ + private final Map managedSessions = new HashMap(); + + /** + * Creates a new instance. + */ + public IoServiceListenerSupport() + { + } + + /** + * Adds a new listener. + */ + public void add( IoServiceListener listener ) + { + synchronized( listeners ) + { + listeners.add( listener ); + } + } + + /** + * Removes an existing listener. + */ + public void remove( IoServiceListener listener ) + { + synchronized( listeners ) + { + listeners.remove( listener ); + } + } + + public Set getManagedServiceAddresses() + { + return Collections.unmodifiableSet( managedServiceAddresses ); + } + + public boolean isManaged( SocketAddress serviceAddress ) + { + synchronized( managedServiceAddresses ) + { + return managedServiceAddresses.contains( serviceAddress ); + } + } + + public Set getManagedSessions( SocketAddress serviceAddress ) + { + Set sessions; + synchronized( managedSessions ) + { + sessions = ( Set ) managedSessions.get( serviceAddress ); + if( sessions == null ) + { + sessions = new IdentityHashSet(); + } + } + + synchronized( sessions ) + { + return new IdentityHashSet( sessions ); + } + } + + /** + * Calls {@link IoServiceListener#serviceActivated(IoService, SocketAddress, IoHandler, IoServiceConfig)} + * for all registered listeners. + */ + public void fireServiceActivated( + IoService service, SocketAddress serviceAddress, + IoHandler handler, IoServiceConfig config ) + { + synchronized( managedServiceAddresses ) + { + if( !managedServiceAddresses.add( serviceAddress ) ) + { + return; + } + } + + synchronized( listeners ) + { + for( Iterator i = listeners.iterator(); i.hasNext(); ) + { + ( ( IoServiceListener ) i.next() ).serviceActivated( + service, serviceAddress, handler, config ); + } + } + } + + /** + * Calls {@link IoServiceListener#serviceDeactivated(IoService, SocketAddress, IoHandler, IoServiceConfig)} + * for all registered listeners. + */ + public synchronized void fireServiceDeactivated( + IoService service, SocketAddress serviceAddress, + IoHandler handler, IoServiceConfig config ) + { + synchronized( managedServiceAddresses ) + { + if( !managedServiceAddresses.remove( serviceAddress ) ) + { + return; + } + } + + try + { + synchronized( listeners ) + { + for( Iterator i = listeners.iterator(); i.hasNext(); ) + { + ( ( IoServiceListener ) i.next() ).serviceDeactivated( + service, serviceAddress, handler, config ); + } + } + } + finally + { + disconnectSessions( serviceAddress, config ); + } + } + + + /** + * Calls {@link IoServiceListener#sessionCreated(IoSession)} for all registered listeners. + */ + public void fireSessionCreated( IoSession session ) + { + SocketAddress serviceAddress = session.getServiceAddress(); + + // Get the session set. + boolean firstSession = false; + Set sessions; + synchronized( managedSessions ) + { + sessions = ( Set ) managedSessions.get( serviceAddress ); + if( sessions == null ) + { + sessions = new IdentityHashSet(); + managedSessions.put( serviceAddress, sessions ); + firstSession = true; + } + } + + // If already registered, ignore. + synchronized( sessions ) + { + if ( !sessions.add( session ) ) + { + return; + } + } + + // If the first connector session, fire a virtual service activation event. + if( session.getService() instanceof IoConnector && firstSession ) + { + fireServiceActivated( + session.getService(), session.getServiceAddress(), + session.getHandler(), session.getServiceConfig() ); + } + + // Fire session events. + session.getFilterChain().fireSessionCreated( session ); + session.getFilterChain().fireSessionOpened( session); + + // Fire listener events. + synchronized( listeners ) + { + for( Iterator i = listeners.iterator(); i.hasNext(); ) + { + ( ( IoServiceListener ) i.next() ).sessionCreated( session ); + } + } + } + + /** + * Calls {@link IoServiceListener#sessionDestroyed(IoSession)} for all registered listeners. + */ + public void fireSessionDestroyed( IoSession session ) + { + SocketAddress serviceAddress = session.getServiceAddress(); + + // Get the session set. + Set sessions; + boolean lastSession = false; + synchronized( managedSessions ) + { + sessions = ( Set ) managedSessions.get( serviceAddress ); + // Ignore if unknown. + if( sessions == null ) + { + return; + } + + // Try to remove the remaining empty seession set after removal. + synchronized( sessions ) + { + sessions.remove( session ); + if( sessions.isEmpty() ) + { + managedSessions.remove( serviceAddress ); + lastSession = true; + } + } + } + + // Fire session events. + session.getFilterChain().fireSessionClosed( session ); + + // Fire listener events. + try + { + synchronized( listeners ) + { + for( Iterator i = listeners.iterator(); i.hasNext(); ) + { + ( ( IoServiceListener ) i.next() ).sessionDestroyed( session ); + } + } + } + finally + { + // Fire a virtual service deactivation event for the last session of the connector. + //TODO double-check that this is *STILL* the last session. May not be the case + if( session.getService() instanceof IoConnector && lastSession ) + { + fireServiceDeactivated( + session.getService(), session.getServiceAddress(), + session.getHandler(), session.getServiceConfig() ); + } + } + } + + private void disconnectSessions( SocketAddress serviceAddress, IoServiceConfig config ) + { + if( !( config instanceof IoAcceptorConfig ) ) + { + return; + } + + if( !( ( IoAcceptorConfig ) config ).isDisconnectOnUnbind() ) + { + return; + } + + Set sessions; + synchronized( managedSessions ) + { + sessions = ( Set ) managedSessions.get( serviceAddress ); + } + + if( sessions == null ) + { + return; + } + + Set sessionsCopy; + + // Create a copy to avoid ConcurrentModificationException + synchronized( sessions ) + { + sessionsCopy = new IdentityHashSet( sessions ); + } + + final CountDownLatch latch = new CountDownLatch(sessionsCopy.size()); + + for( Iterator i = sessionsCopy.iterator(); i.hasNext(); ) + { + ( ( IoSession ) i.next() ).close().addListener( new IoFutureListener() + { + public void operationComplete( IoFuture future ) + { + latch.countDown(); + } + } ); + } + + try + { + latch.await(); + } + catch( InterruptedException ie ) + { + // Ignored + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java b/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java new file mode 100644 index 0000000000..47f19aa76d --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferFullExeception.java @@ -0,0 +1,48 @@ +package org.apache.mina.filter; + +import org.apache.mina.common.IoFilter;/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 WriteBufferFullExeception extends RuntimeException +{ + private IoFilter.WriteRequest _writeRequest; + + public WriteBufferFullExeception() + { + this(null); + } + + public WriteBufferFullExeception(IoFilter.WriteRequest writeRequest) + { + _writeRequest = writeRequest; + } + + + public void setWriteRequest(IoFilter.WriteRequest writeRequest) + { + _writeRequest = writeRequest; + } + + public IoFilter.WriteRequest getWriteRequest() + { + return _writeRequest; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java b/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.java new file mode 100644 index 0000000000..4e9db9071a --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/filter/WriteBufferLimitFilterBuilder.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.mina.filter; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.DefaultIoFilterChainBuilder; +import org.apache.mina.common.IoFilterAdapter; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.executor.ExecutorFilter; + +import java.util.Iterator; +import java.util.List; + +/** + * This filter will turn the asynchronous filterWrite method in to a blocking send when there are more than + * the prescribed number of messages awaiting filterWrite. It should be used in conjunction with the + * {@link ReadThrottleFilterBuilder} on a server as the blocking writes will allow the read thread to + * cause an Out of Memory exception due to a back log of unprocessed messages. + * + * This is should only be viewed as a temporary work around for DIRMINA-302. + * + * A true solution should not be implemented as a filter as this issue will always occur. On a machine + * where the network is slower than the local producer. + * + * Suggested improvement is to allow implementation of policices on what to do when buffer is full. + * + * They could be: + * Block - As this does + * Wait on a given Future - to drain more of the queue.. in essence this filter with high/low watermarks + * Throw Exception - through the client filterWrite() method to allow them to get immediate feedback on buffer state + * + *

+ *

Usage: + *

+ *


+ * DefaultFilterChainBuilder builder = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( builder );
+ * 
+ *

+ * or + *

+ *


+ * IoFilterChain chain = ...
+ * WriteBufferLimitFilterBuilder filter = new WriteBufferLimitFilterBuilder();
+ * filter.attach( chain );
+ * 
+ * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public class WriteBufferLimitFilterBuilder +{ + public static final String PENDING_SIZE = WriteBufferLimitFilterBuilder.class.getName() + ".pendingSize"; + + private static int DEFAULT_CONNECTION_BUFFER_MESSAGE_COUNT = 5000; + + private volatile boolean throwNotBlock = false; + + private volatile int maximumConnectionBufferCount; + private volatile long maximumConnectionBufferSize; + + private final Object _blockLock = new Object(); + + private int _blockWaiters = 0; + + + public WriteBufferLimitFilterBuilder() + { + this(DEFAULT_CONNECTION_BUFFER_MESSAGE_COUNT); + } + + public WriteBufferLimitFilterBuilder(int maxWriteBufferSize) + { + setMaximumConnectionBufferCount(maxWriteBufferSize); + } + + + /** + * Set the maximum amount pending items in the writeQueue for a given session. + * Changing the value will only take effect when new data is received for a + * connection, including existing connections. Default value is 5000 msgs. + * + * @param maximumConnectionBufferCount New buffer size. Must be > 0 + */ + public void setMaximumConnectionBufferCount(int maximumConnectionBufferCount) + { + this.maximumConnectionBufferCount = maximumConnectionBufferCount; + this.maximumConnectionBufferSize = 0; + } + + public void setMaximumConnectionBufferSize(long maximumConnectionBufferSize) + { + this.maximumConnectionBufferSize = maximumConnectionBufferSize; + this.maximumConnectionBufferCount = 0; + } + + /** + * Attach this filter to the specified filter chain. It will search for the ThreadPoolFilter, and attach itself + * before and after that filter. + * + * @param chain {@link IoFilterChain} to attach self to. + */ + public void attach(IoFilterChain chain) + { + String name = getThreadPoolFilterEntryName(chain.getAll()); + + chain.addBefore(name, getClass().getName() + ".sendlimit", new SendLimit()); + } + + /** + * Attach this filter to the specified builder. It will search for the + * {@link ExecutorFilter}, and attach itself before and after that filter. + * + * @param builder {@link DefaultIoFilterChainBuilder} to attach self to. + */ + public void attach(DefaultIoFilterChainBuilder builder) + { + String name = getThreadPoolFilterEntryName(builder.getAll()); + + builder.addBefore(name, getClass().getName() + ".sendlimit", new SendLimit()); + } + + private String getThreadPoolFilterEntryName(List entries) + { + Iterator i = entries.iterator(); + + while (i.hasNext()) + { + IoFilterChain.Entry entry = (IoFilterChain.Entry) i.next(); + + if (entry.getFilter().getClass().isAssignableFrom(ExecutorFilter.class)) + { + return entry.getName(); + } + } + + throw new IllegalStateException("Chain does not contain a ExecutorFilter"); + } + + + public class SendLimit extends IoFilterAdapter + { + public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception + { + try + { + waitTillSendAllowed(session); + } + catch (WriteBufferFullExeception wbfe) + { + nextFilter.exceptionCaught(session, wbfe); + } + + if (writeRequest.getMessage() instanceof ByteBuffer) + { + increasePendingWriteSize(session, (ByteBuffer) writeRequest.getMessage()); + } + + nextFilter.filterWrite(session, writeRequest); + } + + private void increasePendingWriteSize(IoSession session, ByteBuffer message) + { + synchronized (session) + { + Long pendingSize = getScheduledWriteBytes(session) + message.remaining(); + session.setAttribute(PENDING_SIZE, pendingSize); + } + } + + private boolean sendAllowed(IoSession session) + { + if (session.isClosing()) + { + return true; + } + + int lmswm = maximumConnectionBufferCount; + long lmswb = maximumConnectionBufferSize; + + return (lmswm == 0 || session.getScheduledWriteRequests() < lmswm) + && (lmswb == 0 || getScheduledWriteBytes(session) < lmswb); + } + + private long getScheduledWriteBytes(IoSession session) + { + synchronized (session) + { + Long i = (Long) session.getAttribute(PENDING_SIZE); + return null == i ? 0 : i; + } + } + + private void waitTillSendAllowed(IoSession session) + { + synchronized (_blockLock) + { + if (throwNotBlock) + { + throw new WriteBufferFullExeception(); + } + + _blockWaiters++; + + while (!sendAllowed(session)) + { + try + { + _blockLock.wait(); + } + catch (InterruptedException e) + { + // Ignore. + } + } + _blockWaiters--; + } + } + + public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception + { + if (message instanceof ByteBuffer) + { + decrementPendingWriteSize(session, (ByteBuffer) message); + } + notifyWaitingWriters(); + nextFilter.messageSent(session, message); + } + + private void decrementPendingWriteSize(IoSession session, ByteBuffer message) + { + synchronized (session) + { + session.setAttribute(PENDING_SIZE, getScheduledWriteBytes(session) - message.remaining()); + } + } + + private void notifyWaitingWriters() + { + synchronized (_blockLock) + { + if (_blockWaiters != 0) + { + _blockLock.notifyAll(); + } + } + + } + + }//SentLimit + + +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java b/RC5/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java new file mode 100644 index 0000000000..3f7e206cb4 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.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.mina.filter.codec; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; + +/** + * A {@link ProtocolDecoder} that cumulates the content of received + * buffers to a cumulative buffer to help users implement decoders. + *

+ * If the received {@link ByteBuffer} is only a part of a message. + * decoders should cumulate received buffers to make a message complete or + * to postpone decoding until more buffers arrive. + *

+ * Here is an example decoder that decodes CRLF terminated lines into + * Command objects: + *

+ * public class CRLFTerminatedCommandLineDecoder
+ *         extends CumulativeProtocolDecoder {
+ *
+ *     private Command parseCommand(ByteBuffer in) {
+ *         // Convert the bytes in the specified buffer to a
+ *         // Command object.
+ *         ...
+ *     }
+ *
+ *     protected boolean doDecode(IoSession session, ByteBuffer in,
+ *                                ProtocolDecoderOutput out)
+ *             throws Exception {
+ *
+ *         // Remember the initial position.
+ *         int start = in.position();
+ *
+ *         // Now find the first CRLF in the buffer.
+ *         byte previous = 0;
+ *         while (in.hasRemaining()) {
+ *             byte current = in.get();
+ *
+ *             if (previous == '\r' && current == '\n') {
+ *                 // Remember the current position and limit.
+ *                 int position = in.position();
+ *                 int limit = in.limit();
+ *                 try {
+ *                     in.position(start);
+ *                     in.limit(position);
+ *                     // The bytes between in.position() and in.limit()
+ *                     // now contain a full CRLF terminated line.
+ *                     out.write(parseCommand(in.slice()));
+ *                 } finally {
+ *                     // Set the position to point right after the
+ *                     // detected line and set the limit to the old
+ *                     // one.
+ *                     in.position(position);
+ *                     in.limit(limit);
+ *                 }
+ *                 // Decoded one line; CumulativeProtocolDecoder will
+ *                 // call me again until I return false. So just
+ *                 // return true until there are no more lines in the
+ *                 // buffer.
+ *                 return true;
+ *             }
+ *
+ *             previous = current;
+ *         }
+ *
+ *         // Could not find CRLF in the buffer. Reset the initial
+ *         // position to the one we recorded above.
+ *         in.position(start);
+ *
+ *         return false;
+ *     }
+ * }
+ * 
+ * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public abstract class OurCumulativeProtocolDecoder extends ProtocolDecoderAdapter { + + private static final String BUFFER = OurCumulativeProtocolDecoder.class + .getName() + + ".Buffer"; + + /** + * Creates a new instance. + */ + protected OurCumulativeProtocolDecoder() { + } + + /** + * Cumulates content of in into internal buffer and forwards + * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}. + * doDecode() is invoked repeatedly until it returns false + * and the cumulative buffer is NOT compacted after decoding ends. + * + * @throws IllegalStateException if your doDecode() returned + * true not consuming the cumulative buffer. + */ + public void decode(IoSession session, ByteBuffer in, + ProtocolDecoderOutput out) throws Exception { + boolean usingSessionBuffer = true; + ByteBuffer buf = (ByteBuffer) session.getAttribute(BUFFER); + // If we have a session buffer, append data to that; otherwise + // use the buffer read from the network directly. + if (buf != null) { + buf.put(in); + buf.flip(); + } else { + buf = in; + usingSessionBuffer = false; + } + + for (;;) { + int oldPos = buf.position(); + boolean decoded = doDecode(session, buf, out); + if (decoded) { + if (buf.position() == oldPos) { + throw new IllegalStateException( + "doDecode() can't return true when buffer is not consumed."); + } + + if (!buf.hasRemaining()) { + break; + } + } else { + break; + } + } + + + // if there is any data left that cannot be decoded, we store + // it in a buffer in the session and next time this decoder is + // invoked the session buffer gets appended to + if (buf.hasRemaining()) { + storeRemainingInSession(buf, session); + } else { + if (usingSessionBuffer) + removeSessionBuffer(session); + } + } + + /** + * Implement this method to consume the specified cumulative buffer and + * decode its content into message(s). + * + * @param in the cumulative buffer + * @return true if and only if there's more to decode in the buffer + * and you want to have doDecode method invoked again. + * Return false if remaining data is not enough to decode, + * then this method will be invoked again when more data is cumulated. + * @throws Exception if cannot decode in. + */ + protected abstract boolean doDecode(IoSession session, ByteBuffer in, + ProtocolDecoderOutput out) throws Exception; + + /** + * Releases the cumulative buffer used by the specified session. + * Please don't forget to call super.dispose( session ) when + * you override this method. + */ + public void dispose(IoSession session) throws Exception { + removeSessionBuffer(session); + } + + private void removeSessionBuffer(IoSession session) { + ByteBuffer buf = (ByteBuffer) session.removeAttribute(BUFFER); + if (buf != null) { + buf.release(); + } + } + + private void storeRemainingInSession(ByteBuffer buf, IoSession session) { + ByteBuffer remainingBuf = ByteBuffer.allocate(buf.capacity()); + remainingBuf.setAutoExpand(true); + remainingBuf.order(buf.order()); + remainingBuf.put(buf); + session.setAttribute(BUFFER, remainingBuf); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java b/RC5/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java new file mode 100644 index 0000000000..b8c6f29720 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/filter/codec/QpidProtocolCodecFilter.java @@ -0,0 +1,440 @@ +package org.apache.mina.filter.codec; + + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ + +import org.apache.mina.common.*; +import org.apache.mina.common.support.DefaultWriteFuture; +import org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput; +import org.apache.mina.util.SessionLog; +import org.apache.mina.util.Queue; + + +public class QpidProtocolCodecFilter extends IoFilterAdapter +{ + public static final String ENCODER = QpidProtocolCodecFilter.class.getName() + ".encoder"; + public static final String DECODER = QpidProtocolCodecFilter.class.getName() + ".decoder"; + + private static final Class[] EMPTY_PARAMS = new Class[0]; + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap( new byte[0] ); + + private final ProtocolCodecFactory factory; + + public QpidProtocolCodecFilter( ProtocolCodecFactory factory ) + { + if( factory == null ) + { + throw new NullPointerException( "factory" ); + } + this.factory = factory; + } + + public QpidProtocolCodecFilter( final ProtocolEncoder encoder, final ProtocolDecoder decoder ) + { + if( encoder == null ) + { + throw new NullPointerException( "encoder" ); + } + if( decoder == null ) + { + throw new NullPointerException( "decoder" ); + } + + this.factory = new ProtocolCodecFactory() + { + public ProtocolEncoder getEncoder() + { + return encoder; + } + + public ProtocolDecoder getDecoder() + { + return decoder; + } + }; + } + + public QpidProtocolCodecFilter( final Class encoderClass, final Class decoderClass ) + { + if( encoderClass == null ) + { + throw new NullPointerException( "encoderClass" ); + } + if( decoderClass == null ) + { + throw new NullPointerException( "decoderClass" ); + } + if( !ProtocolEncoder.class.isAssignableFrom( encoderClass ) ) + { + throw new IllegalArgumentException( "encoderClass: " + encoderClass.getName() ); + } + if( !ProtocolDecoder.class.isAssignableFrom( decoderClass ) ) + { + throw new IllegalArgumentException( "decoderClass: " + decoderClass.getName() ); + } + try + { + encoderClass.getConstructor( EMPTY_PARAMS ); + } + catch( NoSuchMethodException e ) + { + throw new IllegalArgumentException( "encoderClass doesn't have a public default constructor." ); + } + try + { + decoderClass.getConstructor( EMPTY_PARAMS ); + } + catch( NoSuchMethodException e ) + { + throw new IllegalArgumentException( "decoderClass doesn't have a public default constructor." ); + } + + this.factory = new ProtocolCodecFactory() + { + public ProtocolEncoder getEncoder() throws Exception + { + return ( ProtocolEncoder ) encoderClass.newInstance(); + } + + public ProtocolDecoder getDecoder() throws Exception + { + return ( ProtocolDecoder ) decoderClass.newInstance(); + } + }; + } + + public void onPreAdd( IoFilterChain parent, String name, IoFilter.NextFilter nextFilter ) throws Exception + { + if( parent.contains( ProtocolCodecFilter.class ) ) + { + throw new IllegalStateException( "A filter chain cannot contain more than one QpidProtocolCodecFilter." ); + } + } + + public void messageReceived( IoFilter.NextFilter nextFilter, IoSession session, Object message ) throws Exception + { + if( !( message instanceof ByteBuffer ) ) + { + nextFilter.messageReceived( session, message ); + return; + } + + ByteBuffer in = ( ByteBuffer ) message; + ProtocolDecoder decoder = getDecoder( session ); + ProtocolDecoderOutput decoderOut = getDecoderOut( session, nextFilter ); + + try + { + decoder.decode( session, in, decoderOut ); + } + catch( Throwable t ) + { + ProtocolDecoderException pde; + if( t instanceof ProtocolDecoderException ) + { + pde = ( ProtocolDecoderException ) t; + } + else + { + pde = new ProtocolDecoderException( t ); + } + pde.setHexdump( in.getHexDump() ); + throw pde; + } + finally + { + // Dispose the decoder if this session is connectionless. + if( session.getTransportType().isConnectionless() ) + { + disposeDecoder( session ); + } + + // Release the read buffer. + in.release(); + + decoderOut.flush(); + } + } + + public void messageSent( IoFilter.NextFilter nextFilter, IoSession session, Object message ) throws Exception + { + if( message instanceof HiddenByteBuffer ) + { + return; + } + + if( !( message instanceof MessageByteBuffer ) ) + { + nextFilter.messageSent( session, message ); + return; + } + + nextFilter.messageSent( session, ( ( MessageByteBuffer ) message ).message ); + } + + public void filterWrite( IoFilter.NextFilter nextFilter, IoSession session, IoFilter.WriteRequest writeRequest ) throws Exception + { + Object message = writeRequest.getMessage(); + if( message instanceof ByteBuffer ) + { + nextFilter.filterWrite( session, writeRequest ); + return; + } + + ProtocolEncoder encoder = getEncoder( session ); + ProtocolEncoderOutputImpl encoderOut = getEncoderOut( session, nextFilter, writeRequest ); + + try + { + encoder.encode( session, message, encoderOut ); + encoderOut.flush(); + nextFilter.filterWrite( + session, + new IoFilter.WriteRequest( + new MessageByteBuffer( writeRequest.getMessage() ), + writeRequest.getFuture(), writeRequest.getDestination() ) ); + } + catch( Throwable t ) + { + ProtocolEncoderException pee; + if( t instanceof ProtocolEncoderException ) + { + pee = ( ProtocolEncoderException ) t; + } + else + { + pee = new ProtocolEncoderException( t ); + } + throw pee; + } + finally + { + // Dispose the encoder if this session is connectionless. + if( session.getTransportType().isConnectionless() ) + { + disposeEncoder( session ); + } + } + } + + public void sessionClosed( IoFilter.NextFilter nextFilter, IoSession session ) throws Exception + { + // Call finishDecode() first when a connection is closed. + ProtocolDecoder decoder = getDecoder( session ); + ProtocolDecoderOutput decoderOut = getDecoderOut( session, nextFilter ); + try + { + decoder.finishDecode( session, decoderOut ); + } + catch( Throwable t ) + { + ProtocolDecoderException pde; + if( t instanceof ProtocolDecoderException ) + { + pde = ( ProtocolDecoderException ) t; + } + else + { + pde = new ProtocolDecoderException( t ); + } + throw pde; + } + finally + { + // Dispose all. + disposeEncoder( session ); + disposeDecoder( session ); + + decoderOut.flush(); + } + + nextFilter.sessionClosed( session ); + } + + private ProtocolEncoder getEncoder( IoSession session ) throws Exception + { + ProtocolEncoder encoder = ( ProtocolEncoder ) session.getAttribute( ENCODER ); + if( encoder == null ) + { + encoder = factory.getEncoder(); + session.setAttribute( ENCODER, encoder ); + } + return encoder; + } + + private ProtocolEncoderOutputImpl getEncoderOut( IoSession session, IoFilter.NextFilter nextFilter, IoFilter.WriteRequest writeRequest ) + { + return new ProtocolEncoderOutputImpl( session, nextFilter, writeRequest ); + } + + private ProtocolDecoder getDecoder( IoSession session ) throws Exception + { + ProtocolDecoder decoder = ( ProtocolDecoder ) session.getAttribute( DECODER ); + if( decoder == null ) + { + decoder = factory.getDecoder(); + session.setAttribute( DECODER, decoder ); + } + return decoder; + } + + private ProtocolDecoderOutput getDecoderOut( IoSession session, IoFilter.NextFilter nextFilter ) + { + return new SimpleProtocolDecoderOutput( session, nextFilter ); + } + + private void disposeEncoder( IoSession session ) + { + ProtocolEncoder encoder = ( ProtocolEncoder ) session.removeAttribute( ENCODER ); + if( encoder == null ) + { + return; + } + + try + { + encoder.dispose( session ); + } + catch( Throwable t ) + { + SessionLog.warn( + session, + "Failed to dispose: " + encoder.getClass().getName() + + " (" + encoder + ')' ); + } + } + + private void disposeDecoder( IoSession session ) + { + ProtocolDecoder decoder = ( ProtocolDecoder ) session.removeAttribute( DECODER ); + if( decoder == null ) + { + return; + } + + try + { + decoder.dispose( session ); + } + catch( Throwable t ) + { + SessionLog.warn( + session, + "Falied to dispose: " + decoder.getClass().getName() + + " (" + decoder + ')' ); + } + } + + private static class HiddenByteBuffer extends ByteBufferProxy + { + private HiddenByteBuffer( ByteBuffer buf ) + { + super( buf ); + } + } + + private static class MessageByteBuffer extends ByteBufferProxy + { + private final Object message; + + private MessageByteBuffer( Object message ) + { + super( EMPTY_BUFFER ); + this.message = message; + } + + public void acquire() + { + // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message + } + + public void release() + { + // no-op since we are wraping a zero-byte buffer, this instance is to just curry the message + } + } + + private static class ProtocolEncoderOutputImpl implements ProtocolEncoderOutput + { + private ByteBuffer buffer; + + private final IoSession session; + private final IoFilter.NextFilter nextFilter; + private final IoFilter.WriteRequest writeRequest; + + public ProtocolEncoderOutputImpl( IoSession session, IoFilter.NextFilter nextFilter, IoFilter.WriteRequest writeRequest ) + { + this.session = session; + this.nextFilter = nextFilter; + this.writeRequest = writeRequest; + } + + + + public void write( ByteBuffer buf ) + { + if(buffer != null) + { + flush(); + } + buffer = buf; + } + + public void mergeAll() + { + } + + public WriteFuture flush() + { + WriteFuture future = null; + if( buffer == null ) + { + return null; + } + else + { + ByteBuffer buf = buffer; + // Flush only when the buffer has remaining. + if( buf.hasRemaining() ) + { + future = doFlush( buf ); + } + + } + + return future; + } + + + protected WriteFuture doFlush( ByteBuffer buf ) + { + WriteFuture future = new DefaultWriteFuture( session ); + nextFilter.filterWrite( + session, + new IoFilter.WriteRequest( + buf, + future, writeRequest.getDestination() ) ); + return future; + } + } +} + diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java new file mode 100644 index 0000000000..e5360d32e0 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketAcceptor.java @@ -0,0 +1,547 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoAcceptor; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.support.BaseIoAcceptor; +import org.apache.mina.util.Queue; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.NamePreservingRunnable; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +/** + * {@link IoAcceptor} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public class MultiThreadSocketAcceptor extends SocketAcceptor +{ + /** + * @noinspection StaticNonFinalField + */ + private static volatile int nextId = 0; + + private final Executor executor; + private final Object lock = new Object(); + private final int id = nextId ++; + private final String threadName = "SocketAcceptor-" + id; + private final Map channels = new HashMap(); + + private final Queue registerQueue = new Queue(); + private final Queue cancelQueue = new Queue(); + + private final MultiThreadSocketIoProcessor[] ioProcessors; + private final int processorCount; + + /** + * @noinspection FieldAccessedSynchronizedAndUnsynchronized + */ + private Selector selector; + private Worker worker; + private int processorDistributor = 0; + + /** + * Create an acceptor with a single processing thread using a NewThreadExecutor + */ + public MultiThreadSocketAcceptor() + { + this( 1, new NewThreadExecutor() ); + } + + /** + * Create an acceptor with the desired number of processing threads + * + * @param processorCount Number of processing threads + * @param executor Executor to use for launching threads + */ + public MultiThreadSocketAcceptor( int processorCount, Executor executor ) + { + if( processorCount < 1 ) + { + throw new IllegalArgumentException( "Must have at least one processor" ); + } + + this.executor = executor; + this.processorCount = processorCount; + ioProcessors = new MultiThreadSocketIoProcessor[processorCount]; + + for( int i = 0; i < processorCount; i++ ) + { + ioProcessors[i] = new MultiThreadSocketIoProcessor( "SocketAcceptorIoProcessor-" + id + "." + i, executor ); + } + } + + + /** + * Binds to the specified address and handles incoming connections with the specified + * handler. Backlog value is configured to the value of backlog property. + * + * @throws IOException if failed to bind + */ + public void bind( SocketAddress address, IoHandler handler, IoServiceConfig config ) throws IOException + { + if( handler == null ) + { + throw new NullPointerException( "handler" ); + } + + if( address != null && !( address instanceof InetSocketAddress ) ) + { + throw new IllegalArgumentException( "Unexpected address type: " + address.getClass() ); + } + + if( config == null ) + { + config = getDefaultConfig(); + } + + RegistrationRequest request = new RegistrationRequest( address, handler, config ); + + synchronized( registerQueue ) + { + registerQueue.push( request ); + } + + startupWorker(); + + selector.wakeup(); + + synchronized( request ) + { + while( !request.done ) + { + try + { + request.wait(); + } + catch( InterruptedException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + } + } + } + + if( request.exception != null ) + { + throw request.exception; + } + } + + + private synchronized void startupWorker() throws IOException + { + synchronized( lock ) + { + if( worker == null ) + { + selector = Selector.open(); + worker = new Worker(); + + executor.execute( new NamePreservingRunnable( worker ) ); + } + } + } + + public void unbind( SocketAddress address ) + { + if( address == null ) + { + throw new NullPointerException( "address" ); + } + + CancellationRequest request = new CancellationRequest( address ); + + try + { + startupWorker(); + } + catch( IOException e ) + { + // IOException is thrown only when Worker thread is not + // running and failed to open a selector. We simply throw + // IllegalArgumentException here because we can simply + // conclude that nothing is bound to the selector. + throw new IllegalArgumentException( "Address not bound: " + address ); + } + + synchronized( cancelQueue ) + { + cancelQueue.push( request ); + } + + selector.wakeup(); + + synchronized( request ) + { + while( !request.done ) + { + try + { + request.wait(); + } + catch( InterruptedException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + } + } + } + + if( request.exception != null ) + { + request.exception.fillInStackTrace(); + + throw request.exception; + } + } + + + private class Worker implements Runnable + { + public void run() + { + Thread.currentThread().setName(MultiThreadSocketAcceptor.this.threadName ); + + for( ; ; ) + { + try + { + int nKeys = selector.select(); + + registerNew(); + + if( nKeys > 0 ) + { + processSessions( selector.selectedKeys() ); + } + + cancelKeys(); + + if( selector.keys().isEmpty() ) + { + synchronized( lock ) + { + if( selector.keys().isEmpty() && + registerQueue.isEmpty() && + cancelQueue.isEmpty() ) + { + worker = null; + try + { + selector.close(); + } + catch( IOException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + } + finally + { + selector = null; + } + break; + } + } + } + } + catch( IOException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + + try + { + Thread.sleep( 1000 ); + } + catch( InterruptedException e1 ) + { + ExceptionMonitor.getInstance().exceptionCaught( e1 ); + } + } + } + } + + private void processSessions( Set keys ) throws IOException + { + Iterator it = keys.iterator(); + while( it.hasNext() ) + { + SelectionKey key = ( SelectionKey ) it.next(); + + it.remove(); + + if( !key.isAcceptable() ) + { + continue; + } + + ServerSocketChannel ssc = ( ServerSocketChannel ) key.channel(); + + SocketChannel ch = ssc.accept(); + + if( ch == null ) + { + continue; + } + + boolean success = false; + try + { + + RegistrationRequest req = ( RegistrationRequest ) key.attachment(); + + MultiThreadSocketSessionImpl session = new MultiThreadSocketSessionImpl( + MultiThreadSocketAcceptor.this, nextProcessor(), getListeners(), + req.config, ch, req.handler, req.address ); + + // New Interface +// SocketSessionImpl session = new SocketSessionImpl( +// SocketAcceptor.this, nextProcessor(), getListeners(), +// req.config, ch, req.handler, req.address ); + + + getFilterChainBuilder().buildFilterChain( session.getFilterChain() ); + req.config.getFilterChainBuilder().buildFilterChain( session.getFilterChain() ); + req.config.getThreadModel().buildFilterChain( session.getFilterChain() ); + session.getIoProcessor().addNew( session ); + success = true; + } + catch( Throwable t ) + { + ExceptionMonitor.getInstance().exceptionCaught( t ); + } + finally + { + if( !success ) + { + ch.close(); + } + } + } + } + } + + private MultiThreadSocketIoProcessor nextProcessor() + { + return ioProcessors[processorDistributor++ % processorCount]; + } + + + private void registerNew() + { + if( registerQueue.isEmpty() ) + { + return; + } + + for( ; ; ) + { + RegistrationRequest req; + + synchronized( registerQueue ) + { + req = ( RegistrationRequest ) registerQueue.pop(); + } + + if( req == null ) + { + break; + } + + ServerSocketChannel ssc = null; + + try + { + ssc = ServerSocketChannel.open(); + ssc.configureBlocking( false ); + + // Configure the server socket, + SocketAcceptorConfig cfg; + if( req.config instanceof SocketAcceptorConfig ) + { + cfg = ( SocketAcceptorConfig ) req.config; + } + else + { + cfg = ( SocketAcceptorConfig ) getDefaultConfig(); + } + + ssc.socket().setReuseAddress( cfg.isReuseAddress() ); + ssc.socket().setReceiveBufferSize( + ( ( SocketSessionConfig ) cfg.getSessionConfig() ).getReceiveBufferSize() ); + + // and bind. + ssc.socket().bind( req.address, cfg.getBacklog() ); + if( req.address == null || req.address.getPort() == 0 ) + { + req.address = ( InetSocketAddress ) ssc.socket().getLocalSocketAddress(); + } + ssc.register( selector, SelectionKey.OP_ACCEPT, req ); + + synchronized( channels ) + { + channels.put( req.address, ssc ); + } + + getListeners().fireServiceActivated( + this, req.address, req.handler, req.config ); + } + catch( IOException e ) + { + req.exception = e; + } + finally + { + synchronized( req ) + { + req.done = true; + + req.notifyAll(); + } + + if( ssc != null && req.exception != null ) + { + try + { + ssc.close(); + } + catch( IOException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + } + } + } + } + } + + + private void cancelKeys() + { + if( cancelQueue.isEmpty() ) + { + return; + } + + for( ; ; ) + { + CancellationRequest request; + + synchronized( cancelQueue ) + { + request = ( CancellationRequest ) cancelQueue.pop(); + } + + if( request == null ) + { + break; + } + + ServerSocketChannel ssc; + synchronized( channels ) + { + ssc = ( ServerSocketChannel ) channels.remove( request.address ); + } + + // close the channel + try + { + if( ssc == null ) + { + request.exception = new IllegalArgumentException( "Address not bound: " + request.address ); + } + else + { + SelectionKey key = ssc.keyFor( selector ); + request.registrationRequest = ( RegistrationRequest ) key.attachment(); + key.cancel(); + + selector.wakeup(); // wake up again to trigger thread death + + ssc.close(); + } + } + catch( IOException e ) + { + ExceptionMonitor.getInstance().exceptionCaught( e ); + } + finally + { + synchronized( request ) + { + request.done = true; + request.notifyAll(); + } + + if( request.exception == null ) + { + getListeners().fireServiceDeactivated( + this, request.address, + request.registrationRequest.handler, + request.registrationRequest.config ); + } + } + } + } + + private static class RegistrationRequest + { + private InetSocketAddress address; + private final IoHandler handler; + private final IoServiceConfig config; + private IOException exception; + private boolean done; + + private RegistrationRequest( SocketAddress address, IoHandler handler, IoServiceConfig config ) + { + this.address = ( InetSocketAddress ) address; + this.handler = handler; + this.config = config; + } + } + + + private static class CancellationRequest + { + private final SocketAddress address; + private boolean done; + private RegistrationRequest registrationRequest; + private RuntimeException exception; + + private CancellationRequest( SocketAddress address ) + { + this.address = address; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java new file mode 100644 index 0000000000..b1612840db --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java @@ -0,0 +1,486 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoConnector; +import org.apache.mina.common.IoConnectorConfig; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.support.AbstractIoFilterChain; +import org.apache.mina.common.support.DefaultConnectFuture; +import org.apache.mina.util.NamePreservingRunnable; +import org.apache.mina.util.NewThreadExecutor; +import org.apache.mina.util.Queue; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Set; + +/** + * {@link IoConnector} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public class MultiThreadSocketConnector extends SocketConnector +{ + /** @noinspection StaticNonFinalField */ + private static volatile int nextId = 0; + + private final Object lock = new Object(); + private final int id = nextId++; + private final String threadName = "SocketConnector-" + id; + private SocketConnectorConfig defaultConfig = new SocketConnectorConfig(); + private final Queue connectQueue = new Queue(); + private final MultiThreadSocketIoProcessor[] ioProcessors; + private final int processorCount; + private final Executor executor; + + /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */ + private Selector selector; + private Worker worker; + private int processorDistributor = 0; + private int workerTimeout = 60; // 1 min. + + /** Create a connector with a single processing thread using a NewThreadExecutor */ + public MultiThreadSocketConnector() + { + this(1, new NewThreadExecutor()); + } + + /** + * Create a connector with the desired number of processing threads + * + * @param processorCount Number of processing threads + * @param executor Executor to use for launching threads + */ + public MultiThreadSocketConnector(int processorCount, Executor executor) + { + if (processorCount < 1) + { + throw new IllegalArgumentException("Must have at least one processor"); + } + + this.executor = executor; + this.processorCount = processorCount; + ioProcessors = new MultiThreadSocketIoProcessor[processorCount]; + + for (int i = 0; i < processorCount; i++) + { + ioProcessors[i] = new MultiThreadSocketIoProcessor("SocketConnectorIoProcessor-" + id + "." + i, executor); + } + } + + /** + * How many seconds to keep the connection thread alive between connection requests + * + * @return Number of seconds to keep connection thread alive + */ + public int getWorkerTimeout() + { + return workerTimeout; + } + + /** + * Set how many seconds the connection worker thread should remain alive once idle before terminating itself. + * + * @param workerTimeout Number of seconds to keep thread alive. Must be >=0 + */ + public void setWorkerTimeout(int workerTimeout) + { + if (workerTimeout < 0) + { + throw new IllegalArgumentException("Must be >= 0"); + } + this.workerTimeout = workerTimeout; + } + + public ConnectFuture connect(SocketAddress address, IoHandler handler, IoServiceConfig config) + { + return connect(address, null, handler, config); + } + + public ConnectFuture connect(SocketAddress address, SocketAddress localAddress, + IoHandler handler, IoServiceConfig config) + { + if (address == null) + { + throw new NullPointerException("address"); + } + if (handler == null) + { + throw new NullPointerException("handler"); + } + + if (!(address instanceof InetSocketAddress)) + { + throw new IllegalArgumentException("Unexpected address type: " + + address.getClass()); + } + + if (localAddress != null && !(localAddress instanceof InetSocketAddress)) + { + throw new IllegalArgumentException("Unexpected local address type: " + + localAddress.getClass()); + } + + if (config == null) + { + config = getDefaultConfig(); + } + + SocketChannel ch = null; + boolean success = false; + try + { + ch = SocketChannel.open(); + ch.socket().setReuseAddress(true); + if (localAddress != null) + { + ch.socket().bind(localAddress); + } + + ch.configureBlocking(false); + + if (ch.connect(address)) + { + DefaultConnectFuture future = new DefaultConnectFuture(); + newSession(ch, handler, config, future); + success = true; + return future; + } + + success = true; + } + catch (IOException e) + { + return DefaultConnectFuture.newFailedFuture(e); + } + finally + { + if (!success && ch != null) + { + try + { + ch.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + + ConnectionRequest request = new ConnectionRequest(ch, handler, config); + synchronized (lock) + { + try + { + startupWorker(); + } + catch (IOException e) + { + try + { + ch.close(); + } + catch (IOException e2) + { + ExceptionMonitor.getInstance().exceptionCaught(e2); + } + + return DefaultConnectFuture.newFailedFuture(e); + } + } + + synchronized (connectQueue) + { + connectQueue.push(request); + } + selector.wakeup(); + + return request; + } + + private synchronized void startupWorker() throws IOException + { + if (worker == null) + { + selector = Selector.open(); + worker = new Worker(); + executor.execute(new NamePreservingRunnable(worker)); + } + } + + private void registerNew() + { + if (connectQueue.isEmpty()) + { + return; + } + + for (; ;) + { + ConnectionRequest req; + synchronized (connectQueue) + { + req = (ConnectionRequest) connectQueue.pop(); + } + + if (req == null) + { + break; + } + + SocketChannel ch = req.channel; + try + { + ch.register(selector, SelectionKey.OP_CONNECT, req); + } + catch (IOException e) + { + req.setException(e); + } + } + } + + private void processSessions(Set keys) + { + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isConnectable()) + { + continue; + } + + SocketChannel ch = (SocketChannel) key.channel(); + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + boolean success = false; + try + { + ch.finishConnect(); + newSession(ch, entry.handler, entry.config, entry); + success = true; + } + catch (Throwable e) + { + entry.setException(e); + } + finally + { + key.cancel(); + if (!success) + { + try + { + ch.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + keys.clear(); + } + + private void processTimedOutSessions(Set keys) + { + long currentTime = System.currentTimeMillis(); + Iterator it = keys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (!key.isValid()) + { + continue; + } + + ConnectionRequest entry = (ConnectionRequest) key.attachment(); + + if (currentTime >= entry.deadline) + { + entry.setException(new ConnectException()); + try + { + key.channel().close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + key.cancel(); + } + } + } + } + + private void newSession(SocketChannel ch, IoHandler handler, IoServiceConfig config, ConnectFuture connectFuture) + throws IOException + { + MultiThreadSocketSessionImpl session = + new MultiThreadSocketSessionImpl(this, nextProcessor(), getListeners(), + config, ch, handler, ch.socket().getRemoteSocketAddress()); + + //new interface +// SocketSessionImpl session = new SocketSessionImpl( +// this, nextProcessor(), getListeners(), +// config, ch, handler, ch.socket().getRemoteSocketAddress() ); + try + { + getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getFilterChainBuilder().buildFilterChain(session.getFilterChain()); + config.getThreadModel().buildFilterChain(session.getFilterChain()); + } + catch (Throwable e) + { + throw (IOException) new IOException("Failed to create a session.").initCause(e); + } + + // Set the ConnectFuture of the specified session, which will be + // removed and notified by AbstractIoFilterChain eventually. + session.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, connectFuture ); + + // Forward the remaining process to the SocketIoProcessor. + session.getIoProcessor().addNew(session); + } + + private MultiThreadSocketIoProcessor nextProcessor() + { + return ioProcessors[processorDistributor++ % processorCount]; + } + + private class Worker implements Runnable + { + private long lastActive = System.currentTimeMillis(); + + public void run() + { + Thread.currentThread().setName(MultiThreadSocketConnector.this.threadName); + + for (; ;) + { + try + { + int nKeys = selector.select(1000); + + registerNew(); + + if (nKeys > 0) + { + processSessions(selector.selectedKeys()); + } + + processTimedOutSessions(selector.keys()); + + if (selector.keys().isEmpty()) + { + if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L) + { + synchronized (lock) + { + if (selector.keys().isEmpty() && + connectQueue.isEmpty()) + { + worker = null; + try + { + selector.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + selector = null; + } + break; + } + } + } + } + else + { + lastActive = System.currentTimeMillis(); + } + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + ExceptionMonitor.getInstance().exceptionCaught(e1); + } + } + } + } + } + + private class ConnectionRequest extends DefaultConnectFuture + { + private final SocketChannel channel; + private final long deadline; + private final IoHandler handler; + private final IoServiceConfig config; + + private ConnectionRequest(SocketChannel channel, IoHandler handler, IoServiceConfig config) + { + this.channel = channel; + long timeout; + if (config instanceof IoConnectorConfig) + { + timeout = ((IoConnectorConfig) config).getConnectTimeoutMillis(); + } + else + { + timeout = ((IoConnectorConfig) getDefaultConfig()).getConnectTimeoutMillis(); + } + this.deadline = System.currentTimeMillis() + timeout; + this.handler = handler; + this.config = config; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.java new file mode 100644 index 0000000000..67b8c8d820 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketFilterChain.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.mina.transport.socket.nio; + +import java.io.IOException; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.IoFilter.WriteRequest; +import org.apache.mina.common.support.AbstractIoFilterChain; +import org.apache.mina.util.Queue; + +/** + * An {@link IoFilterChain} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + */ +class MultiThreadSocketFilterChain extends AbstractIoFilterChain { + + MultiThreadSocketFilterChain( IoSession parent ) + { + super( parent ); + } + + protected void doWrite( IoSession session, WriteRequest writeRequest ) + { + MultiThreadSocketSessionImpl s = (MultiThreadSocketSessionImpl) session; + Queue writeRequestQueue = s.getWriteRequestQueue(); + + // SocketIoProcessor.doFlush() will reset it after write is finished + // because the buffer will be passed with messageSent event. + ( ( ByteBuffer ) writeRequest.getMessage() ).mark(); + synchronized( writeRequestQueue ) + { + writeRequestQueue.push( writeRequest ); + if( writeRequestQueue.size() == 1 && session.getTrafficMask().isWritable() ) + { + // Notify SocketIoProcessor only when writeRequestQueue was empty. + s.getIoProcessor().flush( s ); + } + } + } + + protected void doClose( IoSession session ) throws IOException + { + MultiThreadSocketSessionImpl s = (MultiThreadSocketSessionImpl) session; + s.getIoProcessor().remove( s ); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java new file mode 100644 index 0000000000..c23ad8686f --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java @@ -0,0 +1,1026 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock; +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilter.WriteRequest; +import org.apache.mina.common.WriteTimeoutException; +import org.apache.mina.util.IdentityHashSet; +import org.apache.mina.util.NamePreservingRunnable; +import org.apache.mina.util.Queue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Performs all I/O operations for sockets which is connected or bound. This class is used by MINA internally. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $, + */ +class MultiThreadSocketIoProcessor extends SocketIoProcessor +{ + Logger _logger = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class); + Logger _loggerRead = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class + ".Reader"); + Logger _loggerWrite = LoggerFactory.getLogger(MultiThreadSocketIoProcessor.class + ".Writer"); + + private static final long SELECTOR_TIMEOUT = 1000L; + + private int MAX_READ_BYTES_PER_SESSION = 524288; //512K + private int MAX_FLUSH_BYTES_PER_SESSION = 524288; //512K + + private final Object readLock = new Object(); + private final Object writeLock = new Object(); + + private final String threadName; + private final Executor executor; + + private ReentrantLock trafficMaskUpdateLock = new ReentrantLock(); + + /** @noinspection FieldAccessedSynchronizedAndUnsynchronized */ + private volatile Selector selector, writeSelector; + + private final Queue newSessions = new Queue(); + private final Queue removingSessions = new Queue(); + private final BlockingQueue flushingSessions = new LinkedBlockingQueue(); + private final IdentityHashSet flushingSessionsSet = new IdentityHashSet(); + + private final Queue trafficControllingSessions = new Queue(); + + private ReadWorker readWorker; + private WriteWorker writeWorker; + private long lastIdleReadCheckTime = System.currentTimeMillis(); + private long lastIdleWriteCheckTime = System.currentTimeMillis(); + + MultiThreadSocketIoProcessor(String threadName, Executor executor) + { + super(threadName, executor); + this.threadName = threadName; + this.executor = executor; + } + + void addNew(SocketSessionImpl session) throws IOException + { + synchronized (newSessions) + { + newSessions.push(session); + } + + startupWorker(); + + selector.wakeup(); + writeSelector.wakeup(); + } + + void remove(SocketSessionImpl session) throws IOException + { + scheduleRemove(session); + startupWorker(); + selector.wakeup(); + } + + private void startupWorker() throws IOException + { + synchronized (readLock) + { + if (readWorker == null) + { + selector = Selector.open(); + readWorker = new ReadWorker(); + executor.execute(new NamePreservingRunnable(readWorker)); + } + } + + synchronized (writeLock) + { + if (writeWorker == null) + { + writeSelector = Selector.open(); + writeWorker = new WriteWorker(); + executor.execute(new NamePreservingRunnable(writeWorker)); + } + } + + } + + void flush(SocketSessionImpl session) + { + scheduleFlush(session); + Selector selector = this.writeSelector; + + if (selector != null) + { + selector.wakeup(); + } + } + + void updateTrafficMask(SocketSessionImpl session) + { + scheduleTrafficControl(session); + Selector selector = this.selector; + if (selector != null) + { + selector.wakeup(); + } + } + + private void scheduleRemove(SocketSessionImpl session) + { + synchronized (removingSessions) + { + removingSessions.push(session); + } + } + + private void scheduleFlush(SocketSessionImpl session) + { + synchronized (flushingSessionsSet) + { + //if flushingSessions grows to contain Integer.MAX_VALUE sessions + // then this will fail. + if (flushingSessionsSet.add(session)) + { + flushingSessions.offer(session); + } + } + } + + private void scheduleTrafficControl(SocketSessionImpl session) + { + synchronized (trafficControllingSessions) + { + trafficControllingSessions.push(session); + } + } + + private void doAddNewReader() throws InterruptedException + { + if (newSessions.isEmpty()) + { + return; + } + + for (; ;) + { + MultiThreadSocketSessionImpl session; + + synchronized (newSessions) + { + session = (MultiThreadSocketSessionImpl) newSessions.peek(); + } + + if (session == null) + { + break; + } + + SocketChannel ch = session.getChannel(); + + + try + { + + ch.configureBlocking(false); + session.setSelectionKey(ch.register(selector, + SelectionKey.OP_READ, + session)); + + //System.out.println("ReadDebug:"+"Awaiting Registration"); + session.awaitRegistration(); + sessionCreated(session); + } + catch (IOException e) + { + // Clear the AbstractIoFilterChain.CONNECT_FUTURE attribute + // and call ConnectFuture.setException(). + session.getFilterChain().fireExceptionCaught(session, e); + } + } + } + + + private void doAddNewWrite() throws InterruptedException + { + if (newSessions.isEmpty()) + { + return; + } + + for (; ;) + { + MultiThreadSocketSessionImpl session; + + synchronized (newSessions) + { + session = (MultiThreadSocketSessionImpl) newSessions.peek(); + } + + if (session == null) + { + break; + } + + SocketChannel ch = session.getChannel(); + + try + { + ch.configureBlocking(false); + synchronized (flushingSessionsSet) + { + flushingSessionsSet.add(session); + } + + session.setWriteSelectionKey(ch.register(writeSelector, + SelectionKey.OP_WRITE, + session)); + + //System.out.println("WriteDebug:"+"Awaiting Registration"); + session.awaitRegistration(); + sessionCreated(session); + } + catch (IOException e) + { + + // Clear the AbstractIoFilterChain.CONNECT_FUTURE attribute + // and call ConnectFuture.setException(). + session.getFilterChain().fireExceptionCaught(session, e); + } + } + } + + + private void sessionCreated(SocketSessionImpl sessionParam) throws InterruptedException + { + MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) sessionParam; + synchronized (newSessions) + { + if (!session.created()) + { + _logger.debug("Popping new session"); + newSessions.pop(); + + // AbstractIoFilterChain.CONNECT_FUTURE is cleared inside here + // in AbstractIoFilterChain.fireSessionOpened(). + session.getServiceListeners().fireSessionCreated(session); + + session.doneCreation(); + } + } + } + + private void doRemove() + { + if (removingSessions.isEmpty()) + { + return; + } + + for (; ;) + { + MultiThreadSocketSessionImpl session; + + synchronized (removingSessions) + { + session = (MultiThreadSocketSessionImpl) removingSessions.pop(); + } + + if (session == null) + { + break; + } + + SocketChannel ch = session.getChannel(); + SelectionKey key = session.getReadSelectionKey(); + SelectionKey writeKey = session.getWriteSelectionKey(); + + // Retry later if session is not yet fully initialized. + // (In case that Session.close() is called before addSession() is processed) + if (key == null || writeKey == null) + { + scheduleRemove(session); + break; + } + // skip if channel is already closed + if (!key.isValid() || !writeKey.isValid()) + { + continue; + } + + try + { + //System.out.println("ReadDebug:"+"Removing Session: " + System.identityHashCode(session)); + synchronized (readLock) + { + key.cancel(); + } + synchronized (writeLock) + { + writeKey.cancel(); + } + ch.close(); + } + catch (IOException e) + { + session.getFilterChain().fireExceptionCaught(session, e); + } + finally + { + releaseWriteBuffers(session); + session.getServiceListeners().fireSessionDestroyed(session); + } + } + } + + private void processRead(Set selectedKeys) + { + Iterator it = selectedKeys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) key.attachment(); + + synchronized (readLock) + { + if (key.isValid() && key.isReadable() && session.getTrafficMask().isReadable()) + { + read(session); + } + } + + } + + selectedKeys.clear(); + } + + private void processWrite(Set selectedKeys) + { + Iterator it = selectedKeys.iterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + SocketSessionImpl session = (SocketSessionImpl) key.attachment(); + + synchronized (writeLock) + { + if (key.isValid() && key.isWritable() && session.getTrafficMask().isWritable()) + { + + // Clear OP_WRITE + key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE)); + + synchronized (flushingSessionsSet) + { + flushingSessions.offer(session); + } + } + } + } + + selectedKeys.clear(); + } + + private void read(SocketSessionImpl session) + { + + //if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Starting read for Session:" + System.identityHashCode(session)); + } + + int totalReadBytes = 0; + + while (totalReadBytes <= MAX_READ_BYTES_PER_SESSION) + { + ByteBuffer buf = ByteBuffer.allocate(session.getReadBufferSize()); + SocketChannel ch = session.getChannel(); + + try + { + buf.clear(); + + int readBytes = 0; + int ret; + + try + { + while ((ret = ch.read(buf.buf())) > 0) + { + readBytes += ret; + totalReadBytes += ret; + } + } + finally + { + buf.flip(); + } + + + if (readBytes > 0) + { + session.increaseReadBytes(readBytes); + + session.getFilterChain().fireMessageReceived(session, buf); + buf = null; + } + + if (ret <= 0) + { + if (ret == 0) + { + if (readBytes == session.getReadBufferSize()) + { + continue; + } + } + else + { + scheduleRemove(session); + } + + break; + } + } + catch (Throwable e) + { + if (e instanceof IOException) + { + scheduleRemove(session); + } + session.getFilterChain().fireExceptionCaught(session, e); + + //Stop Reading this session. + return; + } + finally + { + if (buf != null) + { + buf.release(); + } + } + }//for + + // if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Read for Session:" + System.identityHashCode(session) + " got: " + totalReadBytes); + } + } + + + private void notifyReadIdleness() + { + // process idle sessions + long currentTime = System.currentTimeMillis(); + if ((currentTime - lastIdleReadCheckTime) >= 1000) + { + lastIdleReadCheckTime = currentTime; + Set keys = selector.keys(); + if (keys != null) + { + for (Iterator it = keys.iterator(); it.hasNext();) + { + SelectionKey key = (SelectionKey) it.next(); + SocketSessionImpl session = (SocketSessionImpl) key.attachment(); + notifyReadIdleness(session, currentTime); + } + } + } + } + + private void notifyWriteIdleness() + { + // process idle sessions + long currentTime = System.currentTimeMillis(); + if ((currentTime - lastIdleWriteCheckTime) >= 1000) + { + lastIdleWriteCheckTime = currentTime; + Set keys = writeSelector.keys(); + if (keys != null) + { + for (Iterator it = keys.iterator(); it.hasNext();) + { + SelectionKey key = (SelectionKey) it.next(); + SocketSessionImpl session = (SocketSessionImpl) key.attachment(); + notifyWriteIdleness(session, currentTime); + } + } + } + } + + private void notifyReadIdleness(SocketSessionImpl session, long currentTime) + { + notifyIdleness0( + session, currentTime, + session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE), + IdleStatus.BOTH_IDLE, + Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE))); + notifyIdleness0( + session, currentTime, + session.getIdleTimeInMillis(IdleStatus.READER_IDLE), + IdleStatus.READER_IDLE, + Math.max(session.getLastReadTime(), session.getLastIdleTime(IdleStatus.READER_IDLE))); + + notifyWriteTimeout(session, currentTime, session + .getWriteTimeoutInMillis(), session.getLastWriteTime()); + } + + private void notifyWriteIdleness(SocketSessionImpl session, long currentTime) + { + notifyIdleness0( + session, currentTime, + session.getIdleTimeInMillis(IdleStatus.BOTH_IDLE), + IdleStatus.BOTH_IDLE, + Math.max(session.getLastIoTime(), session.getLastIdleTime(IdleStatus.BOTH_IDLE))); + notifyIdleness0( + session, currentTime, + session.getIdleTimeInMillis(IdleStatus.WRITER_IDLE), + IdleStatus.WRITER_IDLE, + Math.max(session.getLastWriteTime(), session.getLastIdleTime(IdleStatus.WRITER_IDLE))); + + notifyWriteTimeout(session, currentTime, session + .getWriteTimeoutInMillis(), session.getLastWriteTime()); + } + + private void notifyIdleness0(SocketSessionImpl 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); + } + } + + private void notifyWriteTimeout(SocketSessionImpl session, + long currentTime, + long writeTimeout, long lastIoTime) + { + + MultiThreadSocketSessionImpl sesh = (MultiThreadSocketSessionImpl) session; + SelectionKey key = sesh.getWriteSelectionKey(); + + synchronized (writeLock) + { + if (writeTimeout > 0 + && (currentTime - lastIoTime) >= writeTimeout + && key != null && key.isValid() + && (key.interestOps() & SelectionKey.OP_WRITE) != 0) + { + session.getFilterChain().fireExceptionCaught(session, new WriteTimeoutException()); + } + } + } + + private SocketSessionImpl getNextFlushingSession() + { + return (SocketSessionImpl) flushingSessions.poll(); + } + + private void releaseSession(SocketSessionImpl session) + { + synchronized (session.getWriteRequestQueue()) + { + synchronized (flushingSessionsSet) + { + if (session.getScheduledWriteRequests() > 0) + { + if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Reflush" + System.identityHashCode(session)); + } + flushingSessions.offer(session); + } + else + { + if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Releasing session " + System.identityHashCode(session)); + } + flushingSessionsSet.remove(session); + } + } + } + } + + private void releaseWriteBuffers(SocketSessionImpl session) + { + Queue writeRequestQueue = session.getWriteRequestQueue(); + WriteRequest req; + + //Should this be synchronized? + synchronized (writeRequestQueue) + { + while ((req = (WriteRequest) writeRequestQueue.pop()) != null) + { + try + { + ((ByteBuffer) req.getMessage()).release(); + } + catch (IllegalStateException e) + { + session.getFilterChain().fireExceptionCaught(session, e); + } + finally + { + req.getFuture().setWritten(false); + } + } + } + } + + private void doFlush() + { + MultiThreadSocketSessionImpl session; + + while ((session = (MultiThreadSocketSessionImpl) getNextFlushingSession()) != null) + { + if (!session.isConnected()) + { + releaseWriteBuffers(session); + releaseSession(session); + continue; + } + + SelectionKey key = session.getWriteSelectionKey(); + // Retry later if session is not yet fully initialized. + // (In case that Session.write() is called before addSession() is processed) + if (key == null) + { + scheduleFlush(session); + releaseSession(session); + continue; + } + // skip if channel is already closed + if (!key.isValid()) + { + releaseSession(session); + continue; + } + + try + { + if (doFlush(session)) + { + releaseSession(session); + } + } + catch (IOException e) + { + releaseSession(session); + scheduleRemove(session); + session.getFilterChain().fireExceptionCaught(session, e); + } + + } + + } + + private boolean doFlush(SocketSessionImpl sessionParam) throws IOException + { + MultiThreadSocketSessionImpl session = (MultiThreadSocketSessionImpl) sessionParam; + // Clear OP_WRITE + SelectionKey key = session.getWriteSelectionKey(); + synchronized (writeLock) + { + key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE)); + } + SocketChannel ch = session.getChannel(); + Queue writeRequestQueue = session.getWriteRequestQueue(); + + long totalFlushedBytes = 0; + while (true) + { + WriteRequest req; + + synchronized (writeRequestQueue) + { + req = (WriteRequest) writeRequestQueue.first(); + } + + if (req == null) + { + break; + } + + ByteBuffer buf = (ByteBuffer) req.getMessage(); + if (buf.remaining() == 0) + { + synchronized (writeRequestQueue) + { + writeRequestQueue.pop(); + } + + session.increaseWrittenMessages(); + + buf.reset(); + session.getFilterChain().fireMessageSent(session, req); + continue; + } + + + int writtenBytes = 0; + + // Reported as DIRMINA-362 + //note: todo: fixme: Not sure it is important but if we see NoyYetConnected exceptions or 100% CPU in the kernel then this is it. + if (key.isWritable()) + { + writtenBytes = ch.write(buf.buf()); + totalFlushedBytes += writtenBytes; + } + + if (writtenBytes > 0) + { + session.increaseWrittenBytes(writtenBytes); + } + + if (buf.hasRemaining() || (totalFlushedBytes <= MAX_FLUSH_BYTES_PER_SESSION)) + { + // Kernel buffer is full + synchronized (writeLock) + { + key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); + } + if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Written BF: " + (session.getWrittenBytes() - totalFlushedBytes) + " bytes"); + } + return false; + } + } + + if (_loggerWrite.isDebugEnabled()) + { + //System.out.println("WriteDebug:"+"Written : " + (session.getWrittenBytes() - totalFlushedBytes) + " bytes"); + } + return true; + } + + private void doUpdateTrafficMask() + { + if (trafficControllingSessions.isEmpty() || trafficMaskUpdateLock.isLocked()) + { + return; + } + + // Synchronize over entire operation as this method should be called + // from both read and write thread and we don't want the order of the + // updates to get changed. + trafficMaskUpdateLock.lock(); + try + { + for (; ;) + { + MultiThreadSocketSessionImpl session; + + session = (MultiThreadSocketSessionImpl) trafficControllingSessions.pop(); + + if (session == null) + { + break; + } + + SelectionKey key = session.getReadSelectionKey(); + // Retry later if session is not yet fully initialized. + // (In case that Session.suspend??() or session.resume??() is + // called before addSession() is processed) + if (key == null) + { + scheduleTrafficControl(session); + break; + } + // skip if channel is already closed + if (!key.isValid()) + { + continue; + } + + // The normal is OP_READ and, if there are write requests in the + // session's write queue, set OP_WRITE to trigger flushing. + + //Sset to Read and Write if there is nothing then the cost + // is one loop through the flusher. + int ops = SelectionKey.OP_READ; + + // Now mask the preferred ops with the mask of the current session + int mask = session.getTrafficMask().getInterestOps(); + synchronized (readLock) + { + key.interestOps(ops & mask); + } + //Change key to the WriteSelection Key + key = session.getWriteSelectionKey(); + if (key != null && key.isValid()) + { + Queue writeRequestQueue = session.getWriteRequestQueue(); + synchronized (writeRequestQueue) + { + if (!writeRequestQueue.isEmpty()) + { + ops = SelectionKey.OP_WRITE; + synchronized (writeLock) + { + key.interestOps(ops & mask); + } + } + } + } + } + } + finally + { + trafficMaskUpdateLock.unlock(); + } + + } + + private class WriteWorker implements Runnable + { + + public void run() + { + Thread.currentThread().setName(MultiThreadSocketIoProcessor.this.threadName + "Writer"); + + //System.out.println("WriteDebug:"+"Startup"); + for (; ;) + { + try + { + int nKeys = writeSelector.select(SELECTOR_TIMEOUT); + + doAddNewWrite(); + doUpdateTrafficMask(); + + if (nKeys > 0) + { + //System.out.println("WriteDebug:"+nKeys + " keys from writeselector"); + processWrite(writeSelector.selectedKeys()); + } + else + { + //System.out.println("WriteDebug:"+"No keys from writeselector"); + } + + doRemove(); + notifyWriteIdleness(); + + if (flushingSessionsSet.size() > 0) + { + doFlush(); + } + + if (writeSelector.keys().isEmpty()) + { + synchronized (writeLock) + { + + if (writeSelector.keys().isEmpty() && newSessions.isEmpty()) + { + writeWorker = null; + try + { + writeSelector.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + writeSelector = null; + } + + break; + } + } + } + + } + catch (Throwable t) + { + ExceptionMonitor.getInstance().exceptionCaught(t); + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + ExceptionMonitor.getInstance().exceptionCaught(e1); + } + } + } + //System.out.println("WriteDebug:"+"Shutdown"); + } + + } + + private class ReadWorker implements Runnable + { + + public void run() + { + Thread.currentThread().setName(MultiThreadSocketIoProcessor.this.threadName + "Reader"); + + //System.out.println("ReadDebug:"+"Startup"); + for (; ;) + { + try + { + int nKeys = selector.select(SELECTOR_TIMEOUT); + + doAddNewReader(); + doUpdateTrafficMask(); + + if (nKeys > 0) + { + //System.out.println("ReadDebug:"+nKeys + " keys from selector"); + + processRead(selector.selectedKeys()); + } + else + { + //System.out.println("ReadDebug:"+"No keys from selector"); + } + + + doRemove(); + notifyReadIdleness(); + + if (selector.keys().isEmpty()) + { + + synchronized (readLock) + { + if (selector.keys().isEmpty() && newSessions.isEmpty()) + { + readWorker = null; + try + { + selector.close(); + } + catch (IOException e) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + finally + { + selector = null; + } + + break; + } + } + } + } + catch (Throwable t) + { + ExceptionMonitor.getInstance().exceptionCaught(t); + + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + ExceptionMonitor.getInstance().exceptionCaught(e1); + } + } + } + //System.out.println("ReadDebug:"+"Shutdown"); + } + + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.java new file mode 100644 index 0000000000..043d4800b6 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionConfigImpl.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.mina.transport.socket.nio; + +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoConnectorConfig; +import org.apache.mina.common.support.BaseIoSessionConfig; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; + +/** + * An {@link IoConnectorConfig} for {@link SocketConnector}. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public class MultiThreadSocketSessionConfigImpl extends org.apache.mina.transport.socket.nio.SocketSessionConfigImpl +{ + private static boolean SET_RECEIVE_BUFFER_SIZE_AVAILABLE = false; + private static boolean SET_SEND_BUFFER_SIZE_AVAILABLE = false; + private static boolean GET_TRAFFIC_CLASS_AVAILABLE = false; + private static boolean SET_TRAFFIC_CLASS_AVAILABLE = false; + + private static boolean DEFAULT_REUSE_ADDRESS; + private static int DEFAULT_RECEIVE_BUFFER_SIZE; + private static int DEFAULT_SEND_BUFFER_SIZE; + private static int DEFAULT_TRAFFIC_CLASS; + private static boolean DEFAULT_KEEP_ALIVE; + private static boolean DEFAULT_OOB_INLINE; + private static int DEFAULT_SO_LINGER; + private static boolean DEFAULT_TCP_NO_DELAY; + + static + { + initialize(); + } + + private static void initialize() + { + Socket socket = null; + + socket = new Socket(); + + try + { + DEFAULT_REUSE_ADDRESS = socket.getReuseAddress(); + DEFAULT_RECEIVE_BUFFER_SIZE = socket.getReceiveBufferSize(); + DEFAULT_SEND_BUFFER_SIZE = socket.getSendBufferSize(); + DEFAULT_KEEP_ALIVE = socket.getKeepAlive(); + DEFAULT_OOB_INLINE = socket.getOOBInline(); + DEFAULT_SO_LINGER = socket.getSoLinger(); + DEFAULT_TCP_NO_DELAY = socket.getTcpNoDelay(); + + // Check if setReceiveBufferSize is supported. + try + { + socket.setReceiveBufferSize(DEFAULT_RECEIVE_BUFFER_SIZE); + SET_RECEIVE_BUFFER_SIZE_AVAILABLE = true; + } + catch( SocketException e ) + { + SET_RECEIVE_BUFFER_SIZE_AVAILABLE = false; + } + + // Check if setSendBufferSize is supported. + try + { + socket.setSendBufferSize(DEFAULT_SEND_BUFFER_SIZE); + SET_SEND_BUFFER_SIZE_AVAILABLE = true; + } + catch( SocketException e ) + { + SET_SEND_BUFFER_SIZE_AVAILABLE = false; + } + + // Check if getTrafficClass is supported. + try + { + DEFAULT_TRAFFIC_CLASS = socket.getTrafficClass(); + GET_TRAFFIC_CLASS_AVAILABLE = true; + } + catch( SocketException e ) + { + GET_TRAFFIC_CLASS_AVAILABLE = false; + DEFAULT_TRAFFIC_CLASS = 0; + } + } + catch( SocketException e ) + { + throw new ExceptionInInitializerError(e); + } + finally + { + if( socket != null ) + { + try + { + socket.close(); + } + catch( IOException e ) + { + ExceptionMonitor.getInstance().exceptionCaught(e); + } + } + } + } + + public static boolean isSetReceiveBufferSizeAvailable() { + return SET_RECEIVE_BUFFER_SIZE_AVAILABLE; + } + + public static boolean isSetSendBufferSizeAvailable() { + return SET_SEND_BUFFER_SIZE_AVAILABLE; + } + + public static boolean isGetTrafficClassAvailable() { + return GET_TRAFFIC_CLASS_AVAILABLE; + } + + public static boolean isSetTrafficClassAvailable() { + return SET_TRAFFIC_CLASS_AVAILABLE; + } + + private boolean reuseAddress = DEFAULT_REUSE_ADDRESS; + private int receiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE; + private int sendBufferSize = DEFAULT_SEND_BUFFER_SIZE; + private int trafficClass = DEFAULT_TRAFFIC_CLASS; + private boolean keepAlive = DEFAULT_KEEP_ALIVE; + private boolean oobInline = DEFAULT_OOB_INLINE; + private int soLinger = DEFAULT_SO_LINGER; + private boolean tcpNoDelay = DEFAULT_TCP_NO_DELAY; + + /** + * Creates a new instance. + */ + MultiThreadSocketSessionConfigImpl() + { + } + + public boolean isReuseAddress() + { + return reuseAddress; + } + + public void setReuseAddress( boolean reuseAddress ) + { + this.reuseAddress = reuseAddress; + } + + public int getReceiveBufferSize() + { + return receiveBufferSize; + } + + public void setReceiveBufferSize( int receiveBufferSize ) + { + this.receiveBufferSize = receiveBufferSize; + } + + public int getSendBufferSize() + { + return sendBufferSize; + } + + public void setSendBufferSize( int sendBufferSize ) + { + this.sendBufferSize = sendBufferSize; + } + + public int getTrafficClass() + { + return trafficClass; + } + + public void setTrafficClass( int trafficClass ) + { + this.trafficClass = trafficClass; + } + + public boolean isKeepAlive() + { + return keepAlive; + } + + public void setKeepAlive( boolean keepAlive ) + { + this.keepAlive = keepAlive; + } + + public boolean isOobInline() + { + return oobInline; + } + + public void setOobInline( boolean oobInline ) + { + this.oobInline = oobInline; + } + + public int getSoLinger() + { + return soLinger; + } + + public void setSoLinger( int soLinger ) + { + this.soLinger = soLinger; + } + + public boolean isTcpNoDelay() + { + return tcpNoDelay; + } + + public void setTcpNoDelay( boolean tcpNoDelay ) + { + this.tcpNoDelay = tcpNoDelay; + } + + +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java new file mode 100644 index 0000000000..be4a2d289d --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketSessionImpl.java @@ -0,0 +1,488 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.mina.transport.socket.nio; + +import org.apache.mina.common.IoFilter.WriteRequest; +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.IoSession; +import org.apache.mina.common.IoSessionConfig; +import org.apache.mina.common.RuntimeIOException; +import org.apache.mina.common.TransportType; +import org.apache.mina.common.support.BaseIoSessionConfig; +import org.apache.mina.common.support.IoServiceListenerSupport; +import org.apache.mina.util.Queue; + +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An {@link IoSession} for socket transport (TCP/IP). + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +class MultiThreadSocketSessionImpl extends SocketSessionImpl +{ + private final IoService manager; + private final IoServiceConfig serviceConfig; + private final SocketSessionConfig config = new SessionConfigImpl(); + private final MultiThreadSocketIoProcessor ioProcessor; + private final MultiThreadSocketFilterChain filterChain; + private final SocketChannel ch; + private final Queue writeRequestQueue; + private final IoHandler handler; + private final SocketAddress remoteAddress; + private final SocketAddress localAddress; + private final SocketAddress serviceAddress; + private final IoServiceListenerSupport serviceListeners; + private SelectionKey readKey, writeKey; + private int readBufferSize; + private CountDownLatch registeredReadyLatch = new CountDownLatch(2); + private AtomicBoolean created = new AtomicBoolean(false); + + /** + * Creates a new instance. + */ + MultiThreadSocketSessionImpl( IoService manager, + SocketIoProcessor ioProcessor, + IoServiceListenerSupport listeners, + IoServiceConfig serviceConfig, + SocketChannel ch, + IoHandler defaultHandler, + SocketAddress serviceAddress ) + { + super(manager, ioProcessor, listeners, serviceConfig, ch,defaultHandler,serviceAddress); + this.manager = manager; + this.serviceListeners = listeners; + this.ioProcessor = (MultiThreadSocketIoProcessor) ioProcessor; + this.filterChain = new MultiThreadSocketFilterChain(this); + this.ch = ch; + this.writeRequestQueue = new Queue(); + this.handler = defaultHandler; + this.remoteAddress = ch.socket().getRemoteSocketAddress(); + this.localAddress = ch.socket().getLocalSocketAddress(); + this.serviceAddress = serviceAddress; + this.serviceConfig = serviceConfig; + + // Apply the initial session settings + IoSessionConfig sessionConfig = serviceConfig.getSessionConfig(); + if( sessionConfig instanceof SocketSessionConfig ) + { + SocketSessionConfig cfg = ( SocketSessionConfig ) sessionConfig; + this.config.setKeepAlive( cfg.isKeepAlive() ); + this.config.setOobInline( cfg.isOobInline() ); + this.config.setReceiveBufferSize( cfg.getReceiveBufferSize() ); + this.readBufferSize = cfg.getReceiveBufferSize(); + this.config.setReuseAddress( cfg.isReuseAddress() ); + this.config.setSendBufferSize( cfg.getSendBufferSize() ); + this.config.setSoLinger( cfg.getSoLinger() ); + this.config.setTcpNoDelay( cfg.isTcpNoDelay() ); + + if( this.config.getTrafficClass() != cfg.getTrafficClass() ) + { + this.config.setTrafficClass( cfg.getTrafficClass() ); + } + } + } + + void awaitRegistration() throws InterruptedException + { + registeredReadyLatch.countDown(); + + registeredReadyLatch.await(); + } + + boolean created() throws InterruptedException + { + return created.get(); + } + + void doneCreation() + { + created.getAndSet(true); + } + + public IoService getService() + { + return manager; + } + + public IoServiceConfig getServiceConfig() + { + return serviceConfig; + } + + public IoSessionConfig getConfig() + { + return config; + } + + SocketIoProcessor getIoProcessor() + { + return ioProcessor; + } + + public IoFilterChain getFilterChain() + { + return filterChain; + } + + SocketChannel getChannel() + { + return ch; + } + + IoServiceListenerSupport getServiceListeners() + { + return serviceListeners; + } + + SelectionKey getSelectionKey() + { + return readKey; + } + + SelectionKey getReadSelectionKey() + { + return readKey; + } + + SelectionKey getWriteSelectionKey() + { + return writeKey; + } + + void setSelectionKey(SelectionKey key) + { + this.readKey = key; + } + + void setWriteSelectionKey(SelectionKey key) + { + this.writeKey = key; + } + + public IoHandler getHandler() + { + return handler; + } + + protected void close0() + { + filterChain.fireFilterClose( this ); + } + + Queue getWriteRequestQueue() + { + return writeRequestQueue; + } + + /** + @return int Number of write scheduled write requests + @deprecated + */ + public int getScheduledWriteMessages() + { + return getScheduledWriteRequests(); + } + + public int getScheduledWriteRequests() + { + synchronized( writeRequestQueue ) + { + return writeRequestQueue.size(); + } + } + + public int getScheduledWriteBytes() + { + synchronized( writeRequestQueue ) + { + return writeRequestQueue.byteSize(); + } + } + + protected void write0( WriteRequest writeRequest ) + { + filterChain.fireFilterWrite( this, writeRequest ); + } + + public TransportType getTransportType() + { + return TransportType.SOCKET; + } + + public SocketAddress getRemoteAddress() + { + //This is what I had previously +// return ch.socket().getRemoteSocketAddress(); + return remoteAddress; + } + + public SocketAddress getLocalAddress() + { + //This is what I had previously +// return ch.socket().getLocalSocketAddress(); + return localAddress; + } + + public SocketAddress getServiceAddress() + { + return serviceAddress; + } + + protected void updateTrafficMask() + { + this.ioProcessor.updateTrafficMask( this ); + } + + int getReadBufferSize() + { + return readBufferSize; + } + + private class SessionConfigImpl extends BaseIoSessionConfig implements SocketSessionConfig + { + public boolean isKeepAlive() + { + try + { + return ch.socket().getKeepAlive(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setKeepAlive( boolean on ) + { + try + { + ch.socket().setKeepAlive( on ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public boolean isOobInline() + { + try + { + return ch.socket().getOOBInline(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setOobInline( boolean on ) + { + try + { + ch.socket().setOOBInline( on ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public boolean isReuseAddress() + { + try + { + return ch.socket().getReuseAddress(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setReuseAddress( boolean on ) + { + try + { + ch.socket().setReuseAddress( on ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public int getSoLinger() + { + try + { + return ch.socket().getSoLinger(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setSoLinger( int linger ) + { + try + { + if( linger < 0 ) + { + ch.socket().setSoLinger( false, 0 ); + } + else + { + ch.socket().setSoLinger( true, linger ); + } + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public boolean isTcpNoDelay() + { + try + { + return ch.socket().getTcpNoDelay(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setTcpNoDelay( boolean on ) + { + try + { + ch.socket().setTcpNoDelay( on ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public int getTrafficClass() + { + if( SocketSessionConfigImpl.isGetTrafficClassAvailable() ) + { + try + { + return ch.socket().getTrafficClass(); + } + catch( SocketException e ) + { + // Throw an exception only when setTrafficClass is also available. + if( SocketSessionConfigImpl.isSetTrafficClassAvailable() ) + { + throw new RuntimeIOException( e ); + } + } + } + + return 0; + } + + public void setTrafficClass( int tc ) + { + if( SocketSessionConfigImpl.isSetTrafficClassAvailable() ) + { + try + { + ch.socket().setTrafficClass( tc ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + } + + public int getSendBufferSize() + { + try + { + return ch.socket().getSendBufferSize(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setSendBufferSize( int size ) + { + if( SocketSessionConfigImpl.isSetSendBufferSizeAvailable() ) + { + try + { + ch.socket().setSendBufferSize( size ); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + } + + public int getReceiveBufferSize() + { + try + { + return ch.socket().getReceiveBufferSize(); + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + + public void setReceiveBufferSize( int size ) + { + if( SocketSessionConfigImpl.isSetReceiveBufferSizeAvailable() ) + { + try + { + ch.socket().setReceiveBufferSize( size ); + MultiThreadSocketSessionImpl.this.readBufferSize = size; + } + catch( SocketException e ) + { + throw new RuntimeIOException( e ); + } + } + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java b/RC5/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.java new file mode 100644 index 0000000000..a23e546af5 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/mina/transport/vmpipe/QpidVmPipeConnector.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.mina.transport.vmpipe; + +import java.io.IOException; +import java.net.SocketAddress; + +import org.apache.mina.common.ConnectFuture; +import org.apache.mina.common.ExceptionMonitor; +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.IoHandler; +import org.apache.mina.common.IoServiceConfig; +import org.apache.mina.common.IoSessionConfig; +import org.apache.mina.common.support.AbstractIoFilterChain; +import org.apache.mina.common.support.BaseIoConnector; +import org.apache.mina.common.support.BaseIoConnectorConfig; +import org.apache.mina.common.support.BaseIoSessionConfig; +import org.apache.mina.common.support.DefaultConnectFuture; +import org.apache.mina.transport.vmpipe.support.VmPipe; +import org.apache.mina.transport.vmpipe.support.VmPipeIdleStatusChecker; +import org.apache.mina.transport.vmpipe.support.VmPipeSessionImpl; +import org.apache.mina.util.AnonymousSocketAddress; + +/** + * Connects to {@link IoHandler}s which is bound on the specified + * {@link VmPipeAddress}. + * + * @author The Apache Directory Project (mina-dev@directory.apache.org) + * @version $Rev: 619823 $, $Date: 2008-02-08 10:09:37 +0000 (Fri, 08 Feb 2008) $ + */ +public class QpidVmPipeConnector extends VmPipeConnector +{ + private static final IoSessionConfig CONFIG = new BaseIoSessionConfig() {}; + private final IoServiceConfig defaultConfig = new BaseIoConnectorConfig() + { + public IoSessionConfig getSessionConfig() + { + return CONFIG; + } + }; + + /** + * Creates a new instance. + */ + public QpidVmPipeConnector() + { + } + + public ConnectFuture connect( SocketAddress address, IoHandler handler, IoServiceConfig config ) + { + return connect( address, null, handler, config ); + } + + public ConnectFuture connect( SocketAddress address, SocketAddress localAddress, IoHandler handler, IoServiceConfig config ) + { + if( address == null ) + throw new NullPointerException( "address" ); + if( handler == null ) + throw new NullPointerException( "handler" ); + if( ! ( address instanceof VmPipeAddress ) ) + throw new IllegalArgumentException( + "address must be VmPipeAddress." ); + + if( config == null ) + { + config = getDefaultConfig(); + } + + VmPipe entry = ( VmPipe ) VmPipeAcceptor.boundHandlers.get( address ); + if( entry == null ) + { + return DefaultConnectFuture.newFailedFuture( + new IOException( "Endpoint unavailable: " + address ) ); + } + + DefaultConnectFuture future = new DefaultConnectFuture(); + VmPipeSessionImpl localSession = + new VmPipeSessionImpl( + this, + config, + getListeners(), + new Object(), // lock + new AnonymousSocketAddress(), + handler, + entry ); + + // initialize acceptor session + VmPipeSessionImpl remoteSession = localSession.getRemoteSession(); + try + { + IoFilterChain filterChain = remoteSession.getFilterChain(); + entry.getAcceptor().getFilterChainBuilder().buildFilterChain( filterChain ); + entry.getConfig().getFilterChainBuilder().buildFilterChain( filterChain ); + entry.getConfig().getThreadModel().buildFilterChain( filterChain ); + + // The following sentences don't throw any exceptions. + entry.getListeners().fireSessionCreated( remoteSession ); + VmPipeIdleStatusChecker.getInstance().addSession( remoteSession ); + } + catch( Throwable t ) + { + ExceptionMonitor.getInstance().exceptionCaught( t ); + remoteSession.close(); + } + + + // initialize connector session + try + { + IoFilterChain filterChain = localSession.getFilterChain(); + this.getFilterChainBuilder().buildFilterChain( filterChain ); + config.getFilterChainBuilder().buildFilterChain( filterChain ); + config.getThreadModel().buildFilterChain( filterChain ); + + // The following sentences don't throw any exceptions. + localSession.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, future ); + getListeners().fireSessionCreated( localSession ); + VmPipeIdleStatusChecker.getInstance().addSession( localSession); + } + catch( Throwable t ) + { + future.setException( t ); + } + + + + return future; + } + + public IoServiceConfig getDefaultConfig() + { + return defaultConfig; + } +} \ No newline at end of file diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.java new file mode 100644 index 0000000000..1b2eabdc86 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelClosedException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQChannelClosedException indicates that an operation cannot be performed becauase a channel has been closed. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents a failed operation on a closed channel. + *
+ * + * @todo Does this duplicate AMQChannelException? + */ +public class AMQChannelClosedException extends AMQException +{ + public AMQChannelClosedException(AMQConstant errorCode, String msg, Throwable cause) + { + super(errorCode, msg, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelException.java new file mode 100644 index 0000000000..ef9420ba87 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQChannelException.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; + +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQChannelException indicates that an error that requires the channel to be closed has occurred. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents an error that rquires the channel to be closed. + *
+ * + * @todo Does this duplicate AMQChannelClosedException? + */ +public class AMQChannelException extends AMQException +{ + private final int _classId; + private final int _methodId; + /* AMQP version for which exception ocurred */ + private final byte major; + private final byte minor; + + public AMQChannelException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor, + Throwable cause) + { + super(errorCode, msg, cause); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQFrame getCloseFrame(int channel) + { + MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor)); + return new AMQFrame(channel, reg.createChannelCloseBody(getErrorCode() == null ? AMQConstant.INTERNAL_ERROR.getCode() : getErrorCode().getCode(), new AMQShortString(getMessage()),_classId,_methodId)); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.java new file mode 100644 index 0000000000..b2ce3c1b32 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionClosedException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQConnectionClosedException indicates that a connection has been closed. + * + *

This exception is really used as an event, in order that the method handler that raises it creates an event + * which is propagated to the io handler, in order to notify it of the connection closure. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents a the closure of a connection. + *
+ * + * @todo Should review where exceptions-as-events + */ +public class AMQConnectionClosedException extends AMQException +{ + public AMQConnectionClosedException(AMQConstant errorCode, String msg, Throwable cause) + { + super(errorCode, msg, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionException.java new file mode 100644 index 0000000000..afd415b1eb --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionException.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; + +import org.apache.qpid.framing.*; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQConnectionException indicates that an error that requires the channel to be closed has occurred. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents an error that rquires the channel to be closed. + *
+ * + * @todo Does this duplicate AMQChannelClosedException? + */ +public class AMQConnectionException extends AMQException +{ + private final int _classId; + private final int _methodId; + + /** AMQP version for which exception ocurred, major code. */ + private final byte major; + + /** AMQP version for which exception ocurred, minor code. */ + private final byte minor; + + boolean _closeConnetion; + + public AMQConnectionException(AMQConstant errorCode, String msg, int classId, int methodId, byte major, byte minor, + Throwable cause) + { + super(errorCode, msg, cause); + _classId = classId; + _methodId = methodId; + this.major = major; + this.minor = minor; + } + + public AMQFrame getCloseFrame(int channel) + { + MethodRegistry reg = MethodRegistry.getMethodRegistry(new ProtocolVersion(major,minor)); + return new AMQFrame(channel, + reg.createConnectionCloseBody(getErrorCode().getCode(), + new AMQShortString(getMessage()), + _classId, + _methodId)); + + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.java new file mode 100644 index 0000000000..fa69f7f91b --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQConnectionFailureException.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; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQConnectionFailureException indicates that a connection to a broker could not be formed. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to connect to a broker. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQConnectionFailureException extends AMQException +{ + Collection _exceptions; + + public AMQConnectionFailureException(String message, Throwable cause) + { + super(null, message, cause); + } + + public AMQConnectionFailureException(AMQConstant errorCode, String message, Throwable cause) + { + super(errorCode, message, cause); + } + + public AMQConnectionFailureException(String message, Collection exceptions) + { + // Blah, I hate ? but java won't let super() be anything other than the first thing, sorry... + super (null, message, exceptions.isEmpty() ? null : exceptions.iterator().next()); + this._exceptions = exceptions; + } + + public Collection getLinkedExceptions() + { + return _exceptions; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.java new file mode 100644 index 0000000000..5ec5c42ab9 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQDisconnectedException.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; + +/** + * AMQDisconnectedException indicates that a broker disconnected without failover. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents disconnection without failover by the broker. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQDisconnectedException extends AMQException +{ + public AMQDisconnectedException(String msg, Throwable cause) + { + super(null, msg, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQException.java new file mode 100644 index 0000000000..be335c5dba --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQException.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; + +import javax.management.JMException; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQException forms the root exception of all exceptions relating to the AMQ protocol. It provides space to associate + * a required AMQ error code with the exception, which is a numeric value, with a meaning defined by the protocol. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents an exception condition associated with an AMQ protocol status code. + *
+ * + * @todo This exception class is also used as a generic exception throughout Qpid code. This usage may not be strictly + * correct if this is to signify a protocol exception. Should review. + */ +public class AMQException extends Exception +{ + /** Holds the AMQ error code constant associated with this exception. */ + private AMQConstant _errorCode; + + /** + * Creates an exception with an optional error code, optional message and optional underlying cause. + * + * @param errorCode The error code. May be null if not to be set. + * @param msg The exception message. May be null if not to be set. + * @param cause The underlying cause of the exception. May be null if not to be set. + */ + public AMQException(AMQConstant errorCode, String msg, Throwable cause) + { + super(((msg == null) ? "" : msg) + ((errorCode == null) ? "" : (" [error code " + errorCode + "]")), cause); + _errorCode = errorCode; + } + + /* + * Deprecated constructors brought from M2.1 + */ + @Deprecated + public AMQException (String msg) + { + this(null, (msg == null) ? "" : msg); + } + + @Deprecated + public AMQException (AMQConstant errorCode, String msg) + { + this(errorCode, (msg == null) ? "" : msg, null); + } + + @Deprecated + public AMQException(String msg, Throwable cause) + { + this(null, msg, cause); + } + + + /** + * Gets the AMQ protocol exception code associated with this exception. + * + * @return The AMQ protocol exception code associated with this exception. + */ + public AMQConstant getErrorCode() + { + return _errorCode; + } + + public boolean isHardError() + { + return true; + } + + /** + * Rethrown this exception as a new exception. + * + * Attempt to create a new exception of the same class if they hav the default constructor of: + * {AMQConstant.class, String.class, Throwable.class} + * + * Individual subclasses may override as requried to create a new instance. + * + */ + public AMQException cloneForCurrentThread() + { + Class amqeClass = this.getClass(); + Class[] paramClasses = {AMQConstant.class, String.class, Throwable.class}; + Object[] params = {getErrorCode(), getMessage(), this}; + + AMQException newAMQE; + + try + { + newAMQE = (AMQException) amqeClass.getConstructor(paramClasses).newInstance(params); + } + catch (Exception creationException) + { + newAMQE = new AMQException(getErrorCode(), getMessage(), this); + } + + return newAMQE; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.java new file mode 100644 index 0000000000..baca2a4773 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidArgumentException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQInvalidArgumentException indicates that an invalid argument has been passed to an AMQP method. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents an error due to an invalid argument being passed to an AMQP method. + *
+ */ +public class AMQInvalidArgumentException extends AMQException +{ + public AMQInvalidArgumentException(String message, Throwable cause) + { + super(AMQConstant.INVALID_ARGUMENT, message, cause); + } + + public boolean isHardError() + { + return false; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.java new file mode 100644 index 0000000000..c117968a29 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQInvalidRoutingKeyException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQInvalidRoutingKeyException indicates an error with a routing key having an invalid format. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents a format error in a routing key. + *
+ */ +public class AMQInvalidRoutingKeyException extends AMQException +{ + public AMQInvalidRoutingKeyException(String message, Throwable cause) + { + super(AMQConstant.INVALID_ROUTING_KEY, message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.java new file mode 100644 index 0000000000..a0574efa72 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQPInvalidClassException.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; + +/** + * AMQPInvalidClassException indicates an error when trying to store an illegally typed argument in a field table. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents illegal argument type for field table values. + *
+ * + * @todo Could just re-use an exising exception like IllegalArgumentException or ClassCastException. + */ +public class AMQPInvalidClassException extends RuntimeException +{ + public AMQPInvalidClassException(String s) + { + super(s); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java new file mode 100644 index 0000000000..bbc569839a --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQProtocolException.java @@ -0,0 +1,38 @@ +package org.apache.qpid; + +import org.apache.qpid.protocol.AMQConstant; + +/* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT 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 AMQProtocolException extends AMQException +{ + /** + * Constructor for a Protocol Exception + *

This is the only provided constructor and the parameters have to be + * set to null when they are unknown. + * + * @param msg A description of the reason of this exception . + * @param errorCode A string specifyin the error code of this exception. + * @param cause The linked Execption. + */ + public AMQProtocolException(AMQConstant errorCode, String msg, Throwable cause) + { + super(errorCode, msg, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.java new file mode 100644 index 0000000000..4ae8282af5 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQTimeoutException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQTimeoutException indicates that an expected response from a broker took too long. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Indicates that an expected response from a broker took too long. + *
+ */ +public class AMQTimeoutException extends AMQException +{ + public AMQTimeoutException(String message, Throwable cause) + { + super(AMQConstant.REQUEST_TIMEOUT, message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.java new file mode 100644 index 0000000000..01a569b693 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQUndeliveredException.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; + +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQUndeliveredException indicates that a message, marked immediate or mandatory, could not be delivered. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failure to delivery a message that must be delivered. + *
+ */ +public class AMQUndeliveredException extends AMQException +{ + private Object _bounced; + + public AMQUndeliveredException(AMQConstant errorCode, String msg, Object bounced, Throwable cause) + { + super(errorCode, msg, cause); + + _bounced = bounced; + } + + public Object getUndeliveredMessage() + { + return _bounced; + } + + public boolean isHardError() + { + return false; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.java new file mode 100644 index 0000000000..0eefc03016 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQUnknownExchangeType.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; + +/** + * AMQUnknownExchangeType represents coding error where unknown exchange type requested from exchange factory. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents unknown exchange type request. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Represent coding error, where unknown exchange type is requested by passing a string parameter. Use a type safe + * enum for the exchange type, or replace with IllegalArgumentException. Should be runtime. + */ +public class AMQUnknownExchangeType extends AMQException +{ + public AMQUnknownExchangeType(String message, Throwable cause) + { + super(null, message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java b/RC5/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.java new file mode 100644 index 0000000000..eee3e6afcf --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/AMQUnresolvedAddressException.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; + +/** + * AMQUnresolvedAddressException indicates failure to resolve a socket address. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents failre to resolve a socket address. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Why replace java.nio.UnresolvedAddressException with this? This is checked, which may explain why, but it + * doesn't wrap the underlying exception. + */ +public class AMQUnresolvedAddressException extends AMQException +{ + String _broker; + + public AMQUnresolvedAddressException(String message, String broker, Throwable cause) + { + super(null, message, cause); + _broker = broker; + } + + public String toString() + { + return super.toString() + " Broker, \"" + _broker + "\""; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetails.java b/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetails.java new file mode 100644 index 0000000000..63f67a7857 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetails.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; + +import java.util.Map; + +/** + * This interface represents a broker and provides the basic information + * required for opening a connection with a broker. + */ +public interface BrokerDetails +{ + /** + * Those are the supported protocols + */ + public static final String PROTOCOL_TCP = "tcp"; + public static final String PROTOCOL_TLS = "tls"; + + public static final String VIRTUAL_HOST = "virtualhost"; + public static final String CLIENT_ID = "client_id"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + + /** + * Get the broker host name. + * + * @return The broker host name. + */ + public String getHost(); + + /** + * Get the broker port number. + * + * @return The broker port number. + */ + public int getPort(); + + /** + * Get the virtual host to connect to. + * + * @return The virtual host of this broker. + */ + public String getVirtualHost(); + + /** + * Get the user name. + * + * @return The user name + */ + public String getUserName(); + + /** + * Get the user password + * + * @return The user password + */ + public String getPassword(); + + /** + * Get the protocol used to connect to hise broker. + * + * @return the protocol used to connect to the broker. + */ + public String getProtocol(); + + /** + * Set the broker host name. + * + * @param host The broker host name. + */ + public void setHost(String host); + + /** + * Set the broker port number. + * + * @param port The broker port number. + */ + public void setPort(int port); + + /** + * Set the virtual host to connect to. + * + * @param virtualHost The virtual host of this broker. + */ + public void setVirtualHost(String virtualHost); + + /** + * Set the user name. + * + * @param userName The user name + */ + public void setUserName(String userName); + + /** + * Set the user password + * + * @param password The user password + */ + public void setPassword(String password); + + /** + * Set the protocol used to connect to hise broker. + * + * @param protocol the protocol used to connect to the broker. + */ + public void setProtocol(String protocol); + + /** + * Ex: keystore path + * + * @return the Properties associated with this connection. + */ + public Map getProperties(); + + /** + * Sets the properties associated with this connection + * + * @param props the new p[roperties. + */ + public void setProperties(Map props); + + public void setProperty(String key,String value); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java b/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java new file mode 100644 index 0000000000..201d43e21f --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/BrokerDetailsImpl.java @@ -0,0 +1,264 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid; + +import java.util.HashMap; +import java.util.Map; + +/** + * Implements the interface BrokerDetails + */ +public class BrokerDetailsImpl implements BrokerDetails +{ + //--- Those are the default values + private final String DEFAULT_USERNAME = "guest"; + private final String DEFAULT_PASSWORD = "guest"; + private final String DEFAULT_VIRTUALHOST = ""; + + //---- The brker details + private String _host; + private int _port = 0; + private String _virtualHost; + private String _userName = DEFAULT_USERNAME; + private String _password = DEFAULT_PASSWORD; + private String _protocol; + private Map _props = new HashMap(); + ; + + //--- Constructors + + public BrokerDetailsImpl() + { + } + + /** + * Create a new broker details given all the reuqired information + * + * @param protocol The protocol used for this broker connection + * @param host The host name. + * @param port The port number. + * @param virtualHost The virtual host. + * @param userName The user name. + * @param password The user password. + */ + public BrokerDetailsImpl(String protocol, String host, int port, String virtualHost, String userName, + String password, Map props) + { + _protocol = protocol; + _host = host; + _port = port; + _virtualHost = virtualHost; + _userName = userName; + _password = password; + _props = props; + } + + /** + * Create a new broker details given the host name and the procol type, + * default values are used for the other details. + * + * @param protocol The protocol used for this broker connection + * @param host The host name. + */ + public BrokerDetailsImpl(String protocol, String host) + { + _protocol = protocol; + _host = host; + _virtualHost = DEFAULT_VIRTUALHOST; + _userName = DEFAULT_USERNAME; + _password = DEFAULT_PASSWORD; + } + + //--- API BrokerDetails + /** + * Get the user password + * + * @return The user password + */ + public String getPassword() + { + return _password; + } + + /** + * Get the broker host name. + * + * @return The broker host name. + */ + public String getHost() + { + return _host; + } + + /** + * Get the broker port number. + * + * @return The broker port number. + */ + public int getPort() + { + if (_port == 0) + { + if (getProtocol().equals(BrokerDetails.PROTOCOL_TCP)) + { + _port = 5672; + } + else if (getProtocol().equals(BrokerDetails.PROTOCOL_TLS)) + { + _port = 5555; + } + } + return _port; + } + + /** + * Get the virtual host to connect to. + * + * @return The virtual host of this broker. + */ + public String getVirtualHost() + { + return _virtualHost; + } + + /** + * Get the user name. + * + * @return The user name + */ + public String getUserName() + { + return _userName; + } + + /** + * Get the protocol used to connect to hise broker. + * + * @return the protocol used to connect to the broker. + */ + public String getProtocol() + { + return _protocol; + } + + /** + * Set the broker host name. + * + * @param host The broker host name. + */ + public void setHost(String host) + { + _host = host; + } + + /** + * Set the broker port number. + * + * @param port The broker port number. + */ + public void setPort(int port) + { + _port = port; + } + + /** + * Set the virtual host to connect to. + * + * @param virtualHost The virtual host of this broker. + */ + public void setVirtualHost(String virtualHost) + { + _virtualHost = virtualHost; + } + + /** + * Set the user name. + * + * @param userName The user name + */ + public void setUserName(String userName) + { + _userName = userName; + } + + /** + * Set the user password + * + * @param password The user password + */ + public void setPassword(String password) + { + _password = password; + } + + /** + * Set the protocol used to connect to hise broker. + * + * @param protocol the protocol used to connect to the broker. + */ + public void setProtocol(String protocol) + { + _protocol = protocol; + } + + /** + * Ex: keystore path + * + * @return the Properties associated with this connection. + */ + public Map getProperties() + { + return _props; + } + + /** + * Sets the properties associated with this connection + * + * @param props + */ + public void setProperties(Map props) + { + _props = props; + } + + public void setProperty(String key, String value) + { + _props.put(key, value); + } + + public String toString() + { + StringBuilder b = new StringBuilder(); + b.append("[username=" + _userName); + b.append(",password=" + _password); + b.append(",transport=" + _protocol); + b.append(",host=" + _host); + b.append(",port=" + getPort() + "]"); + b.append(" - Properties["); + if (_props != null) + { + for (String k : _props.keySet()) + { + b.append(k + "=" + _props.get(k) + ","); + } + } + b.append("]"); + + return b.toString(); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java b/RC5/java/common/src/main/java/org/apache/qpid/ConsoleOutput.java new file mode 100644 index 0000000000..f17782ebf4 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/ConsoleOutput.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; + +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.Sender; + +import static org.apache.qpid.transport.util.Functions.*; + + +/** + * ConsoleOutput + * + * @author Rafael H. Schloming + */ + +public class ConsoleOutput implements Sender +{ + + public void send(ByteBuffer buf) + { + System.out.println(str(buf)); + } + + public void flush() + { + // pass + } + + public void close() + { + System.out.println("CLOSED"); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/ErrorCode.java b/RC5/java/common/src/main/java/org/apache/qpid/ErrorCode.java new file mode 100644 index 0000000000..0549869e71 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/ErrorCode.java @@ -0,0 +1,123 @@ +package org.apache.qpid; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +public enum ErrorCode +{ + //Qpid specific - for the time being + UNDEFINED(1,"undefined",true), + MESSAGE_REJECTED(2,"message_rejected",true), + CONNECTION_ERROR(3,"connection was closed",true), + UNSUPPORTED_PROTOCOL(4, "protocol version is unsupported", true), + + //This might change in the spec, the error class is not applicable + NO_ERROR(200,"reply-success",true), + + //From the spec + CONTENT_TOO_LARGE(311,"content-too-large",false), + NO_ROUTE(312,"no-route",false), + NO_CONSUMERS(313,"content-consumers",false), + CONNECTION_FORCED(320,"connection-forced",true), + INVALID_PATH(402,"invalid-path",true), + ACCESS_REFUSED(403,"access-refused",false), + NOT_FOUND(404,"not-found",false), + RESOURCE_LOCKED(405,"resource-locked",false), + PRE_CONDITION_FAILED(406,"precondition-failed",false), + + FRAME_ERROR(501,"frame_error",true), + SYNTAX_ERROR(502,"syntax_error",true), + COMMAND_INVALID(503,"command_invalid",true), + SESSION_ERROR(504,"sesion_error",true), + NOT_ALLOWED(530,"not_allowed",true), + NOT_IMPLEMENTED(540,"not_implemented",true), + INTERNAL_ERROR(541,"internal_error",true), + INVALID_ARGUMENT(542,"invalid_argument",true); + + private int _code; + private String _desc; + private boolean _hardError; + + private ErrorCode(int code,String desc,boolean hardError) + { + _code = code; + _desc= desc; + _hardError = hardError; + } + + public int getCode() + { + return _code; + } + + public String getDesc() + { + return _desc; + } + + private boolean isHardError() + { + return _hardError; + } + + public static ErrorCode get(int code) + { + switch(code) + { + case 200 : return NO_ERROR; + case 311 : return CONTENT_TOO_LARGE; + case 312 : return NO_ROUTE; + case 313 : return NO_CONSUMERS; + case 320 : return CONNECTION_FORCED; + case 402 : return INVALID_PATH; + case 403 : return ACCESS_REFUSED; + case 404 : return NOT_FOUND; + case 405 : return RESOURCE_LOCKED; + case 406 : return PRE_CONDITION_FAILED; + case 501 : return FRAME_ERROR; + case 502 : return SYNTAX_ERROR; + case 503 : return COMMAND_INVALID; + case 504 : return SESSION_ERROR; + case 530 : return NOT_ALLOWED; + case 540 : return NOT_IMPLEMENTED; + case 541 : return INTERNAL_ERROR; + case 542 : return INVALID_ARGUMENT; + + default : return UNDEFINED; + } + } + } + +/* + + + + The server could not complete the method because of an internal error. The server may require + intervention by an operator in order to resume normal operations. + + + + + + An invalid or illegal argument was passed to a method, and the operation could not proceed. + + +*/ diff --git a/RC5/java/common/src/main/java/org/apache/qpid/QpidConfig.java b/RC5/java/common/src/main/java/org/apache/qpid/QpidConfig.java new file mode 100644 index 0000000000..9c8019f109 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/QpidConfig.java @@ -0,0 +1,111 @@ +package org.apache.qpid; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +/** + * API to configure the Security parameters of the client. + * The user can choose to pick the config from any source + * and set it using this class. + * + */ +public class QpidConfig +{ + private static QpidConfig _instance = new QpidConfig(); + + private SecurityMechanism[] securityMechanisms = + new SecurityMechanism[]{new SecurityMechanism("PLAIN","org.apache.qpid.security.UsernamePasswordCallbackHandler"), + new SecurityMechanism("CRAM_MD5","org.apache.qpid.security.UsernamePasswordCallbackHandler")}; + + private SaslClientFactory[] saslClientFactories = + new SaslClientFactory[]{new SaslClientFactory("AMQPLAIN","org.apache.qpid.security.amqplain.AmqPlainSaslClientFactory")}; + + private QpidConfig(){} + + public static QpidConfig get() + { + return _instance; + } + + public void setSecurityMechanisms(SecurityMechanism... securityMechanisms) + { + this.securityMechanisms = securityMechanisms; + } + + public SecurityMechanism[] getSecurityMechanisms() + { + return securityMechanisms; + } + + public void setSaslClientFactories(SaslClientFactory... saslClientFactories) + { + this.saslClientFactories = saslClientFactories; + } + + public SaslClientFactory[] getSaslClientFactories() + { + return saslClientFactories; + } + + public class SecurityMechanism + { + String type; + String handler; + + SecurityMechanism(String type,String handler) + { + this.type = type; + this.handler = handler; + } + + public String getHandler() + { + return handler; + } + + public String getType() + { + return type; + } + } + + public class SaslClientFactory + { + String type; + String factoryClass; + + SaslClientFactory(String type,String factoryClass) + { + this.type = type; + this.factoryClass = factoryClass; + } + + public String getFactoryClass() + { + return factoryClass; + } + + public String getType() + { + return type; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/QpidException.java b/RC5/java/common/src/main/java/org/apache/qpid/QpidException.java new file mode 100644 index 0000000000..8503adaef8 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/QpidException.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; + +public class QpidException extends Exception +{ + /** + * AMQP error code + */ + private ErrorCode _errorCode; + + /** + * Constructor for a Qpid Exception. + *

This is the only provided constructor and the parameters have to be set to null when + * they are unknown. + * @param message A description of the reason of this exception . + * @param errorCode A string specifyin the error code of this exception. + * @param cause The linked Execption. * + * + */ + public QpidException(String message, ErrorCode errorCode, Throwable cause) + { + super(message, cause); + _errorCode = errorCode; + } + + /*hack to get rid of a compile error from a generated class + public QpidException(String message, String errorCode, Throwable cause) + { + + }*/ + + /** + * Get this execption error code. + * + * @return This exception error code. + */ + public ErrorCode getErrorCode() + { + return _errorCode; + } +} + diff --git a/RC5/java/common/src/main/java/org/apache/qpid/SecurityHelper.java b/RC5/java/common/src/main/java/org/apache/qpid/SecurityHelper.java new file mode 100644 index 0000000000..dda5a6506d --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/SecurityHelper.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; + +import java.io.UnsupportedEncodingException; +import java.util.HashSet; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.qpid.security.AMQPCallbackHandler; +import org.apache.qpid.security.CallbackHandlerRegistry; + +public class SecurityHelper +{ + public static String chooseMechanism(List mechanisms) throws UnsupportedEncodingException + { + HashSet mechanismSet = new HashSet(); + for (Object m : mechanisms) + { + mechanismSet.add(m); + } + + 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; + } + + public static AMQPCallbackHandler createCallbackHandler(String mechanism, String username,String password) + throws QpidException + { + Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism); + try + { + Object instance = mechanismClass.newInstance(); + AMQPCallbackHandler cbh = (AMQPCallbackHandler) instance; + cbh.initialise(username,password); + return cbh; + } + catch (Exception e) + { + throw new QpidException("Unable to create callback handler: " + e,ErrorCode.UNDEFINED, e.getCause()); + } + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/SerialException.java b/RC5/java/common/src/main/java/org/apache/qpid/SerialException.java new file mode 100644 index 0000000000..c59a6af779 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/SerialException.java @@ -0,0 +1,40 @@ +package org.apache.qpid; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 exception is used by the serial class (imp RFC 1982) + * + */ +public class SerialException extends ArithmeticException +{ + /** + * Constructs an SerialException with the specified + * detail message. + * + * @param message The exception message. + */ + public SerialException(String message) + { + super(message); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/ToyBroker.java b/RC5/java/common/src/main/java/org/apache/qpid/ToyBroker.java new file mode 100644 index 0000000000..db84b83adb --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/ToyBroker.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; + +import org.apache.qpid.transport.*; +import org.apache.qpid.transport.network.mina.MinaHandler; + +import static org.apache.qpid.transport.util.Functions.str; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; + + +/** + * ToyBroker + * + * @author Rafael H. Schloming + */ + +class ToyBroker extends SessionDelegate +{ + + private ToyExchange exchange; + private Map consumers = new ConcurrentHashMap(); + + public ToyBroker(ToyExchange exchange) + { + this.exchange = exchange; + } + + public void messageAcquire(Session context, MessageAcquire struct) + { + System.out.println("\n==================> messageAcquire " ); + context.executionResult((int) struct.getId(), new Acquired(struct.getTransfers())); + } + + @Override public void queueDeclare(Session ssn, QueueDeclare qd) + { + exchange.createQueue(qd.getQueue()); + System.out.println("\n==================> declared queue: " + qd.getQueue() + "\n"); + } + + @Override public void exchangeBind(Session ssn, ExchangeBind qb) + { + exchange.bindQueue(qb.getExchange(), qb.getBindingKey(),qb.getQueue()); + System.out.println("\n==================> bound queue: " + qb.getQueue() + " with binding key " + qb.getBindingKey() + "\n"); + } + + @Override public void queueQuery(Session ssn, QueueQuery qq) + { + QueueQueryResult result = new QueueQueryResult().queue(qq.getQueue()); + ssn.executionResult((int) qq.getId(), result); + } + + @Override public void messageSubscribe(Session ssn, MessageSubscribe ms) + { + Consumer c = new Consumer(); + c._queueName = ms.getQueue(); + consumers.put(ms.getDestination(),c); + System.out.println("\n==================> message subscribe : " + ms.getDestination() + " queue: " + ms.getQueue() + "\n"); + } + + @Override public void messageFlow(Session ssn,MessageFlow struct) + { + Consumer c = consumers.get(struct.getDestination()); + c._credit = struct.getValue(); + System.out.println("\n==================> message flow : " + struct.getDestination() + " credit: " + struct.getValue() + "\n"); + } + + @Override public void messageFlush(Session ssn,MessageFlush struct) + { + System.out.println("\n==================> message flush for consumer : " + struct.getDestination() + "\n"); + checkAndSendMessagesToConsumer(ssn,struct.getDestination()); + } + + @Override public void messageTransfer(Session ssn, MessageTransfer xfr) + { + String dest = xfr.getDestination(); + System.out.println("received transfer " + dest); + Header header = xfr.getHeader(); + DeliveryProperties props = header.get(DeliveryProperties.class); + if (props != null) + { + System.out.println("received headers routing_key " + props.getRoutingKey()); + } + MessageProperties mp = header.get(MessageProperties.class); + System.out.println("MP: " + mp); + if (mp != null) + { + System.out.println(mp.getApplicationHeaders()); + } + + if (exchange.route(dest,props.getRoutingKey(),xfr)) + { + System.out.println("queued " + xfr); + dispatchMessages(ssn); + } + else + { + + if (props == null || !props.getDiscardUnroutable()) + { + RangeSet ranges = new RangeSet(); + ranges.add(xfr.getId()); + ssn.messageReject(ranges, MessageRejectCode.UNROUTABLE, + "no such destination"); + } + } + ssn.processed(xfr); + } + + private void transferMessageToPeer(Session ssn,String dest, MessageTransfer m) + { + System.out.println("\n==================> Transfering message to: " +dest + "\n"); + ssn.messageTransfer(m.getDestination(), MessageAcceptMode.EXPLICIT, + MessageAcquireMode.PRE_ACQUIRED, + m.getHeader(), m.getBody()); + } + + private void dispatchMessages(Session ssn) + { + for (String dest: consumers.keySet()) + { + checkAndSendMessagesToConsumer(ssn,dest); + } + } + + private void checkAndSendMessagesToConsumer(Session ssn,String dest) + { + Consumer c = consumers.get(dest); + LinkedBlockingQueue queue = exchange.getQueue(c._queueName); + MessageTransfer m = queue.poll(); + while (m != null && c._credit>0) + { + transferMessageToPeer(ssn,dest,m); + c._credit--; + m = queue.poll(); + } + } + + // ugly, but who cares :) + // assumes unit is always no of messages, not bytes + // assumes it's credit mode and not window + private class Consumer + { + long _credit; + String _queueName; + } + + public static final void main(String[] args) throws IOException + { + final ToyExchange exchange = new ToyExchange(); + ConnectionDelegate delegate = new ServerDelegate() + { + public SessionDelegate getSessionDelegate() + { + return new ToyBroker(exchange); + } + }; + + MinaHandler.accept("0.0.0.0", 5672, delegate); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/ToyClient.java b/RC5/java/common/src/main/java/org/apache/qpid/ToyClient.java new file mode 100644 index 0000000000..3491af8cd2 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/ToyClient.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; + +import java.nio.*; +import java.util.*; + +import org.apache.qpid.transport.*; +import org.apache.qpid.transport.network.mina.MinaHandler; + + +/** + * ToyClient + * + * @author Rafael H. Schloming + */ + +class ToyClient implements SessionListener +{ + public void opened(Session ssn) {} + + public void exception(Session ssn, SessionException exc) + { + exc.printStackTrace(); + } + + public void message(Session ssn, MessageTransfer xfr) + { + System.out.println("msg: " + xfr); + } + + public void closed(Session ssn) {} + + public static final void main(String[] args) + { + Connection conn = new Connection(); + conn.connect("0.0.0.0", 5672, null, "guest", "guest", false); + Session ssn = conn.createSession(); + ssn.setSessionListener(new ToyClient()); + + ssn.queueDeclare("asdf", null, null); + ssn.sync(); + + Map nested = new LinkedHashMap(); + nested.put("list", Arrays.asList("one", "two", "three")); + Map map = new LinkedHashMap(); + + map.put("str", "this is a string"); + + map.put("+int", 3); + map.put("-int", -3); + map.put("maxint", Integer.MAX_VALUE); + map.put("minint", Integer.MIN_VALUE); + + map.put("+short", (short) 1); + map.put("-short", (short) -1); + map.put("maxshort", (short) Short.MAX_VALUE); + map.put("minshort", (short) Short.MIN_VALUE); + + map.put("float", (float) 3.3); + map.put("double", 4.9); + map.put("char", 'c'); + + map.put("table", nested); + map.put("list", Arrays.asList(1, 2, 3)); + map.put("binary", new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + + ssn.messageTransfer("asdf", MessageAcceptMode.EXPLICIT, + MessageAcquireMode.PRE_ACQUIRED, + new Header(new DeliveryProperties(), + new MessageProperties() + .setApplicationHeaders(map)), + "this is the data"); + + ssn.messageTransfer("fdsa", MessageAcceptMode.EXPLICIT, + MessageAcquireMode.PRE_ACQUIRED, + null, + "this should be rejected"); + ssn.sync(); + + Future future = ssn.queueQuery("asdf"); + System.out.println(future.get().getQueue()); + ssn.sync(); + ssn.close(); + conn.close(); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/ToyExchange.java b/RC5/java/common/src/main/java/org/apache/qpid/ToyExchange.java new file mode 100644 index 0000000000..da6aed9629 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/ToyExchange.java @@ -0,0 +1,154 @@ +package org.apache.qpid; +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.qpid.transport.MessageTransfer; + + +public class ToyExchange +{ + final static String DIRECT = "amq.direct"; + final static String TOPIC = "amq.topic"; + + private Map>> directEx = new HashMap>>(); + private Map>> topicEx = new HashMap>>(); + private Map> queues = new HashMap>(); + + public void createQueue(String name) + { + queues.put(name, new LinkedBlockingQueue()); + } + + public LinkedBlockingQueue getQueue(String name) + { + return queues.get(name); + } + + public void bindQueue(String type,String binding,String queueName) + { + LinkedBlockingQueue queue = queues.get(queueName); + binding = normalizeKey(binding); + if(DIRECT.equals(type)) + { + + if (directEx.containsKey(binding)) + { + List> list = directEx.get(binding); + list.add(queue); + } + else + { + List> list = new LinkedList>(); + list.add(queue); + directEx.put(binding,list); + } + } + else + { + if (topicEx.containsKey(binding)) + { + List> list = topicEx.get(binding); + list.add(queue); + } + else + { + List> list = new LinkedList>(); + list.add(queue); + topicEx.put(binding,list); + } + } + } + + public boolean route(String dest, String routingKey, MessageTransfer msg) + { + List> queues; + if(DIRECT.equals(dest)) + { + queues = directEx.get(routingKey); + } + else + { + queues = matchWildCard(routingKey); + } + if(queues != null && queues.size()>0) + { + System.out.println("Message stored in " + queues.size() + " queues"); + storeMessage(msg,queues); + return true; + } + else + { + System.out.println("Message unroutable " + msg); + return false; + } + } + + private String normalizeKey(String routingKey) + { + if(routingKey.indexOf(".*")>1) + { + return routingKey.substring(0,routingKey.indexOf(".*")); + } + else + { + return routingKey; + } + } + + private List> matchWildCard(String routingKey) + { + List> selected = new ArrayList>(); + + for(String key: topicEx.keySet()) + { + Pattern p = Pattern.compile(key); + Matcher m = p.matcher(routingKey); + if (m.find()) + { + for(LinkedBlockingQueue queue : topicEx.get(key)) + { + selected.add(queue); + } + } + } + + return selected; + } + + private void storeMessage(MessageTransfer msg,List> selected) + { + for(LinkedBlockingQueue queue : selected) + { + queue.offer(msg); + } + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/api/Message.java b/RC5/java/common/src/main/java/org/apache/qpid/api/Message.java new file mode 100644 index 0000000000..df6f279026 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/api/Message.java @@ -0,0 +1,126 @@ +package org.apache.qpid.api; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.qpid.transport.MessageProperties; +import org.apache.qpid.transport.DeliveryProperties; +import org.apache.qpid.transport.Header; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public interface Message +{ + public Header getHeader(); + + public void setHeader(Header header); + + public MessageProperties getMessageProperties(); + + public DeliveryProperties getDeliveryProperties(); + + /** + * This will abstract the underlying message data. + * The Message implementation may not hold all message + * data in memory (especially in the case of large messages) + * + * The appendData function might write data to + *
    + *
  • Memory (Ex: ByteBuffer) + *
  • To Disk + *
  • To Socket (Stream) + *
+ * @param src - the data to append + */ + public void appendData(byte[] src) throws IOException; + + + /** + * This will abstract the underlying message data. + * The Message implementation may not hold all message + * data in memory (especially in the case of large messages) + * + * The appendData function might write data to + *
    + *
  • Memory (Ex: ByteBuffer) + *
  • To Disk + *
  • To Socket (Stream) + *
+ * @param src - the data to append + */ + public void appendData(ByteBuffer src) throws IOException; + + /** + * This will abstract the underlying message data. + * The Message implementation may not hold all message + * data in memory (especially in the case of large messages) + * + * The read function might copy data from + *
    + *
  • From memory (Ex: ByteBuffer) + *
  • From Disk + *
  • From Socket as and when it gets streamed + *
+ * @param target The target byte[] which the data gets copied to + */ + public void readData(byte[] target) throws IOException; + + /** + * * This will abstract the underlying message data. + * The Message implementation may not hold all message + * data in memory (especially in the case of large messages) + * + * The read function might copy data from + *
    + *
  • From memory (Ex: ByteBuffer) + *
  • From Disk + *
  • From Socket as and when it gets streamed + *
+ * + * @return A ByteBuffer containing data + * @throws IOException + */ + public ByteBuffer readData() throws IOException; + + /** + * This should clear the body of the message. + */ + public void clearData(); + + /** + * The provides access to the command Id assigned to the + * message transfer. + * This id is useful when you do + *
    + *
  • For message acquiring - If the transfer happend in no-acquire mode + * you could use this id to accquire it. + *
  • For releasing a message. You can use this id to release an acquired + * message + *
  • For Acknowledging a message - You need to pass this ID, in order to + * acknowledge the message + *
  • For Rejecting a message - You need to pass this ID, in order to reject + * the message. + *
+ * + * @return the message transfer id. + */ + public int getMessageTransferId(); + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.java new file mode 100644 index 0000000000..fa890d0ebb --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQCodecFactory.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.codec; + +import org.apache.mina.filter.codec.ProtocolCodecFactory; +import org.apache.mina.filter.codec.ProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolEncoder; + +/** + * AMQCodecFactory is a Mina codec factory. It supplies the encoders and decoders need to read and write the bytes to + * the wire. + * + *

+ *
CRC Card
Responsibilities Collaborations. + *
Supply the protocol encoder. {@link AMQEncoder} + *
Supply the protocol decoder. {@link AMQDecoder} + *
+ */ +public class AMQCodecFactory implements ProtocolCodecFactory +{ + /** Holds the protocol encoder. */ + private final AMQEncoder _encoder = new AMQEncoder(); + + /** Holds the protocol decoder. */ + private final AMQDecoder _frameDecoder; + + /** + * Creates a new codec factory, specifiying whether it is expected that the first frame of data should be an + * initiation. This is the case for the broker, which always expects to received the protocol initiation on a newly + * connected client. + * + * @param expectProtocolInitiation true if the first frame received is going to be a protocol initiation + * frame, false if it is going to be a standard AMQ data block. + */ + public AMQCodecFactory(boolean expectProtocolInitiation) + { + _frameDecoder = new AMQDecoder(expectProtocolInitiation); + } + + /** + * Gets the AMQP encoder. + * + * @return The AMQP encoder. + */ + public ProtocolEncoder getEncoder() + { + return _encoder; + } + + /** + * Gets the AMQP decoder. + * + * @return The AMQP decoder. + */ + public ProtocolDecoder getDecoder() + { + return _frameDecoder; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.java new file mode 100644 index 0000000000..7eef73f337 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQDecoder.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.codec; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.common.SimpleByteBufferAllocator; +import org.apache.mina.filter.codec.CumulativeProtocolDecoder; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; + +import org.apache.qpid.framing.AMQDataBlockDecoder; +import org.apache.qpid.framing.ProtocolInitiation; + +/** + * AMQDecoder delegates the decoding of AMQP either to a data block decoder, or in the case of new connections, to a + * protocol initiation decoder. It is a cumulative decoder, which means that it can accumulate data to decode in the + * buffer until there is enough data to decode. + * + *

One instance of this class is created per session, so any changes or configuration done at run time to the + * decoder will only affect decoding of the protocol session data to which is it bound. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Delegate protocol initiation to its decoder. {@link ProtocolInitiation.Decoder} + *
Delegate AMQP data to its decoder. {@link AMQDataBlockDecoder} + *
Accept notification that protocol initiation has completed. + *
+ * + * @todo If protocol initiation decoder not needed, then don't create it. Probably not a big deal, but it adds to the + * per-session overhead. + */ +public class AMQDecoder extends CumulativeProtocolDecoder +{ + + private static final String BUFFER = AMQDecoder.class.getName() + ".Buffer"; + + /** Holds the 'normal' AMQP data decoder. */ + private AMQDataBlockDecoder _dataBlockDecoder = new AMQDataBlockDecoder(); + + /** Holds the protocol initiation decoder. */ + private ProtocolInitiation.Decoder _piDecoder = new ProtocolInitiation.Decoder(); + + /** Flag to indicate whether this decoder needs to handle protocol initiation. */ + private boolean _expectProtocolInitiation; + private boolean firstDecode = true; + + /** + * Creates a new AMQP decoder. + * + * @param expectProtocolInitiation true if this decoder needs to handle protocol initiation. + */ + public AMQDecoder(boolean expectProtocolInitiation) + { + _expectProtocolInitiation = expectProtocolInitiation; + } + + /** + * Delegates decoding AMQP from the data buffer that Mina has retrieved from the wire, to the data or protocol + * intiation decoders. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return true if the data was decoded, false if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + protected boolean doDecode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + + boolean decoded; + if (_expectProtocolInitiation + || (firstDecode + && (in.remaining() > 0) + && (in.get(in.position()) == (byte)'A'))) + { + decoded = doDecodePI(session, in, out); + } + else + { + decoded = doDecodeDataBlock(session, in, out); + } + if(firstDecode && decoded) + { + firstDecode = false; + } + return decoded; + } + + /** + * Decodes AMQP data, delegating the decoding to an {@link AMQDataBlockDecoder}. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return true if the data was decoded, false if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + protected boolean doDecodeDataBlock(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + int pos = in.position(); + boolean enoughData = _dataBlockDecoder.decodable(session, in); + in.position(pos); + if (!enoughData) + { + // returning false means it will leave the contents in the buffer and + // call us again when more data has been read + return false; + } + else + { + _dataBlockDecoder.decode(session, in, out); + + return true; + } + } + + /** + * Decodes an AMQP initiation, delegating the decoding to a {@link ProtocolInitiation.Decoder}. + * + * @param session The Mina session. + * @param in The raw byte buffer. + * @param out The Mina object output gatherer to write decoded objects to. + * + * @return true if the data was decoded, false if more is needed and the data should accumulate. + * + * @throws Exception If the data cannot be decoded for any reason. + */ + private boolean doDecodePI(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + boolean enoughData = _piDecoder.decodable(session, in); + if (!enoughData) + { + // returning false means it will leave the contents in the buffer and + // call us again when more data has been read + return false; + } + else + { + _piDecoder.decode(session, in, out); + + return true; + } + } + + /** + * Sets the protocol initation flag, that determines whether decoding is handled by the data decoder of the protocol + * initation decoder. This method is expected to be called with false once protocol initation completes. + * + * @param expectProtocolInitiation true to use the protocol initiation decoder, false to use the + * data decoder. + */ + public void setExpectProtocolInitiation(boolean expectProtocolInitiation) + { + _expectProtocolInitiation = expectProtocolInitiation; + } + + + /** + * Cumulates content of in into internal buffer and forwards + * decoding request to {@link #doDecode(IoSession, ByteBuffer, ProtocolDecoderOutput)}. + * doDecode() is invoked repeatedly until it returns false + * and the cumulative buffer is compacted after decoding ends. + * + * @throws IllegalStateException if your doDecode() returned + * true not consuming the cumulative buffer. + */ + public void decode( IoSession session, ByteBuffer in, + ProtocolDecoderOutput out ) throws Exception + { + ByteBuffer buf = ( ByteBuffer ) session.getAttribute( BUFFER ); + // if we have a session buffer, append data to that otherwise + // use the buffer read from the network directly + if( buf != null ) + { + buf.put( in ); + buf.flip(); + } + else + { + buf = in; + } + + for( ;; ) + { + int oldPos = buf.position(); + boolean decoded = doDecode( session, buf, out ); + if( decoded ) + { + if( buf.position() == oldPos ) + { + throw new IllegalStateException( + "doDecode() can't return true when buffer is not consumed." ); + } + + if( !buf.hasRemaining() ) + { + break; + } + } + else + { + break; + } + } + + // if there is any data left that cannot be decoded, we store + // it in a buffer in the session and next time this decoder is + // invoked the session buffer gets appended to + if ( buf.hasRemaining() ) + { + storeRemainingInSession( buf, session ); + } + else + { + removeSessionBuffer( session ); + } + } + + /** + * Releases the cumulative buffer used by the specified session. + * Please don't forget to call super.dispose( session ) when + * you override this method. + */ + public void dispose( IoSession session ) throws Exception + { + removeSessionBuffer( session ); + } + + private void removeSessionBuffer(IoSession session) + { + ByteBuffer buf = ( ByteBuffer ) session.getAttribute( BUFFER ); + if( buf != null ) + { + buf.release(); + session.removeAttribute( BUFFER ); + } + } + + private static final SimpleByteBufferAllocator SIMPLE_BYTE_BUFFER_ALLOCATOR = new SimpleByteBufferAllocator(); + + private void storeRemainingInSession(ByteBuffer buf, IoSession session) + { + ByteBuffer remainingBuf = SIMPLE_BYTE_BUFFER_ALLOCATOR.allocate( buf.remaining(), false ); + remainingBuf.setAutoExpand( true ); + remainingBuf.put( buf ); + session.setAttribute( BUFFER, remainingBuf ); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.java new file mode 100644 index 0000000000..53f48ae1c8 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/codec/AMQEncoder.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.codec; + +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoder; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; + +import org.apache.qpid.framing.AMQDataBlockEncoder; + +/** + * AMQEncoder delegates encoding of AMQP to a data encoder. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Delegate AMQP encoding. {@link AMQDataBlockEncoder} + *
+ * + * @todo This class just delegates to another, so seems to be pointless. Unless it is going to handle some + * responsibilities in the future, then drop it. + */ +public class AMQEncoder implements ProtocolEncoder +{ + /** The data encoder that is delegated to. */ + private AMQDataBlockEncoder _dataBlockEncoder = new AMQDataBlockEncoder(); + + /** + * Encodes AMQP. + * + * @param session The Mina session. + * @param message The data object to encode. + * @param out The Mina writer to output the raw byte data to. + * + * @throws Exception If the data cannot be encoded for any reason. + */ + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception + { + _dataBlockEncoder.encode(session, message, out); + } + + /** + * Does nothing. Called by Mina to allow this to clean up resources when it is no longer needed. + * + * @param session The Mina session. + */ + public void dispose(IoSession session) + { } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java b/RC5/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.java new file mode 100644 index 0000000000..9ed915cc35 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/common/AMQPFilterTypes.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.common; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Specifies the different filter types for consumers that filter their messages. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent different consumer filter types. + *
+ */ +public enum AMQPFilterTypes +{ + JMS_SELECTOR("x-filter-jms-selector"), + NO_CONSUME("x-filter-no-consume"), + AUTO_CLOSE("x-filter-auto-close"); + + /** The identifying string for the filter type. */ + private final AMQShortString _value; + + /** + * Creates a new filter type from its identifying string. + * + * @param value The identifying string. + */ + AMQPFilterTypes(String value) + { + _value = new AMQShortString(value); + } + + /** + * Gets the identifying string of the filter type. + * + * @return The identifying string of the filter type. + */ + public AMQShortString getValue() + { + return _value; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java b/RC5/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java new file mode 100644 index 0000000000..7371c12519 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/common/ClientProperties.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.common; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Specifies the available client property types that different clients can use to identify themselves with. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Specify the available client property types. + *
+ */ +public enum ClientProperties +{ + instance("instance"), + product("product"), + version("version"), + platform("platform"); + + private final AMQShortString _amqShortString; + + private ClientProperties(String name) + { + _amqShortString = new AMQShortString(name); + } + + + public AMQShortString toAMQShortString() + { + return _amqShortString; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java b/RC5/java/common/src/main/java/org/apache/qpid/common/QpidProperties.java new file mode 100644 index 0000000000..2c783aeaa4 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/common/QpidProperties.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.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; + +/** + * QpidProperties captures the project name, version number, and source code repository revision number from a properties + * file which is generated as part of the build process. Normally, the name and version number are pulled from the module + * name and version number of the Maven build POM, but could come from other sources if the build system is changed. The + * idea behind this, is that every build has these values incorporated directly into its jar file, so that code in the + * wild can be identified, should its origination be forgotten. + * + *

To get the build version of any Qpid code call the {@link #main} method. This version string is usually also + * printed to the console on broker start up. + * + *

+ *
CRC Card
Load build versioning information into the runtime, for code identification purposes. + *
+ * + * @todo Code to locate/load/log properties can be factored into a reusable properties utils class. Avoid having this + * same snippet of loading code scattered in many places. + * + * @todo Could also add a build number property for a sequential build number assigned by an automated build system, for + * build reproducability purposes. + */ +public class QpidProperties +{ + /** Used for debugging purposes. */ + private static final Logger _logger = LoggerFactory.getLogger(QpidProperties.class); + + /** The name of the version properties file to load from the class path. */ + public static final String VERSION_RESOURCE = "qpidversion.properties"; + + /** Defines the name of the product property. */ + public static final String PRODUCT_NAME_PROPERTY = "qpid.name"; + + /** Defines the name of the version property. */ + public static final String RELEASE_VERSION_PROPERTY = "qpid.version"; + + /** Defines the name of the source code revision property. */ + public static final String BUILD_VERSION_PROPERTY = "qpid.svnversion"; + + /** Defines the default value for all properties that cannot be loaded. */ + private static final String DEFAULT = "unknown"; + + /** Holds the product name. */ + private static String productName = DEFAULT; + + /** Holds the product version. */ + private static String releaseVersion = DEFAULT; + + /** Holds the source code revision. */ + private static String buildVersion = DEFAULT; + + // Loads the values from the version properties file. + static + { + Properties props = new Properties(); + + try + { + InputStream propertyStream = QpidProperties.class.getClassLoader().getResourceAsStream(VERSION_RESOURCE); + if (propertyStream == null) + { + _logger.warn("Unable to find resource " + VERSION_RESOURCE + " from classloader"); + } + else + { + props.load(propertyStream); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Dumping QpidProperties"); + for (Map.Entry entry : props.entrySet()) + { + _logger.debug("Property: " + entry.getKey() + " Value: " + entry.getValue()); + } + + _logger.debug("End of property dump"); + } + + productName = readPropertyValue(props, PRODUCT_NAME_PROPERTY); + releaseVersion = readPropertyValue(props, RELEASE_VERSION_PROPERTY); + buildVersion = readPropertyValue(props, BUILD_VERSION_PROPERTY); + } + } + catch (IOException e) + { + // Log a warning about this and leave the values initialized to unknown. + _logger.error("Could not load version.properties resource: " + e, e); + } + } + + /** + * Gets the product name. + * + * @return The product name. + */ + public static String getProductName() + { + return productName; + } + + /** + * Gets the product version. + * + * @return The product version. + */ + public static String getReleaseVersion() + { + return releaseVersion; + } + + /** + * Gets the source code revision. + * + * @return The source code revision. + */ + public static String getBuildVersion() + { + return buildVersion; + } + + /** + * Extracts all of the version information as a printable string. + * + * @return All of the version information as a printable string. + */ + public static String getVersionString() + { + return getProductName() + " - " + getReleaseVersion() + " build: " + getBuildVersion(); + } + + /** + * Helper method to extract a named property from properties. + * + * @param props The properties. + * @param propertyName The named property to extract. + * + * @return The extracted property or a default value if the properties do not contain the named property. + * + * @todo A bit pointless. + */ + private static String readPropertyValue(Properties props, String propertyName) + { + String retVal = (String) props.get(propertyName); + if (retVal == null) + { + retVal = DEFAULT; + } + + return retVal; + } + + /** + * Prints the versioning information to the console. This is extremely usefull for identifying Qpid code in the + * wild, where the origination of the code has been forgotten. + * + * @param args Does not require any arguments. + */ + public static void main(String[] args) + { + System.out.println(getVersionString()); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/configuration/Configured.java b/RC5/java/common/src/main/java/org/apache/qpid/configuration/Configured.java new file mode 100644 index 0000000000..22903888fe --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/configuration/Configured.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.configuration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a field as having a "configured" value injected into it by a configurator. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Configured +{ + /** + * The Commons Configuration path to the configuration element + */ + String path(); + + /** + * The default value to use should the path not be found in the configuration source + */ + String defaultValue(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java b/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.java new file mode 100644 index 0000000000..73a336321c --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyException.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.configuration; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * Indicates a failure to parse a property expansion. See {@link PropertyUtils} for the code that does property + * expansions. + * + *

+ *
CRC Card
Responsibilities Collaboration + *
Represent failure to expand a property name into a value. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class PropertyException extends AMQException +{ + public PropertyException(String message, Throwable cause) + { + super(null, message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java b/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java new file mode 100644 index 0000000000..6e2b25fb2c --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/configuration/PropertyUtils.java @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.configuration; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * PropertyUtils provides helper methods for dealing with Java properties. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Expand system properties into strings with named expansions. + *
+ * + * @todo Make the lookup method generic by passing in the properties to use for the expansion, rather than hard coding + * as system properties. The expansion code has greater potential for re-use that way. + * + * @todo Some more property related code could be added to this utils class, which might more appropriately reside under + * org.apache.qpid.util. For example standardised code to load properties from a resource name, currently found in + * QpidProperties and possibly other places could be moved here. + */ +public class PropertyUtils +{ + /** + * Given a string that contains substrings of the form ${xxx}, looks up the valuea of 'xxx' as a + * system properties and substitutes tham back into the original string, to provide a property value expanded + * string. + * + * @param value The string to be scanned for property references. May be null, in which case this + * method returns immediately with no effect. + * + * @return The original string with the properties replaced, or null if the original string is + * null. + * + * @throws PropertyException If the string contains an opening ${ without a balancing }, + * or if the property to expand does not exist as a system property. + */ + public static String replaceProperties(String value) throws PropertyException + { + if (value == null) + { + return null; + } + + ArrayList fragments = new ArrayList(); + ArrayList propertyRefs = new ArrayList(); + parsePropertyString(value, fragments, propertyRefs); + + StringBuffer sb = new StringBuffer(); + Iterator j = propertyRefs.iterator(); + + for (String fragment : fragments) + { + if (fragment == null) + { + String propertyName = (String) j.next(); + + // try to get it from the project or keys + // Backward compatibility + String replacement = System.getProperty(propertyName); + + if (replacement == null) + { + throw new PropertyException("Property ${" + propertyName + "} has not been set", null); + } + + fragment = replacement; + } + + sb.append(fragment); + } + + return sb.toString(); + } + + /** + * Parses the supplied value for properties which are specified using ${foo} syntax. $X is left as is, and $$ + * specifies a single $. + * + * @param value The property string to parse. + * @param fragments Is populated with the string fragments. A null means "insert a property value here. The number + * of nulls in the list when populated is equal to the size of the propertyRefs list. + * @param propertyRefs Populated with the property names to be added into the final string. + */ + private static void parsePropertyString(String value, ArrayList fragments, ArrayList propertyRefs) + throws PropertyException + { + int prev = 0; + int pos; + // search for the next instance of $ from the 'prev' position + while ((pos = value.indexOf("$", prev)) >= 0) + { + + // if there was any text before this, add it as a fragment + if (pos > 0) + { + fragments.add(value.substring(prev, pos)); + } + // if we are at the end of the string, we tack on a $ + // then move past it + if (pos == (value.length() - 1)) + { + fragments.add("$"); + prev = pos + 1; + } + else if (value.charAt(pos + 1) != '{') + { + // peek ahead to see if the next char is a property or not + // not a property: insert the char as a literal + if (value.charAt(pos + 1) == '$') + { + // two $ map to one $ + fragments.add("$"); + prev = pos + 2; + } + else + { + // $X maps to $X for all values of X!='$' + fragments.add(value.substring(pos, pos + 2)); + prev = pos + 2; + } + } + else + { + // property found, extract its name or bail on a typo + int endName = value.indexOf('}', pos); + if (endName < 0) + { + throw new PropertyException("Syntax error in property: " + value, null); + } + + String propertyName = value.substring(pos + 2, endName); + fragments.add(null); + propertyRefs.add(propertyName); + prev = endName + 1; + } + } + // no more $ signs found + // if there is any tail to the file, append it + if (prev < value.length()) + { + fragments.add(value.substring(prev)); + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java b/RC5/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java new file mode 100644 index 0000000000..49effc2dae --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/dtx/XidImpl.java @@ -0,0 +1,250 @@ +/* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.dtx; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.qpid.QpidException; + +import javax.transaction.xa.Xid; + +import java.io.*; + +/** + * Implements javax.transaction.dtx.Xid + */ +public class XidImpl implements Xid +{ + /** + * this session's logger + */ + private static final Logger _logger = LoggerFactory.getLogger(XidImpl.class); + + /** + * the transaction branch identifier part of XID as an array of bytes + */ + private byte[] _branchQualifier; + + /** + * the format identifier part of the XID. + */ + private int _formatID; + + /** + * the global transaction identifier part of XID as an array of bytes. + */ + private byte[] _globalTransactionID; + + //--- Constructors + + /** + * Create new Xid. + * this is an empty constructor. + */ + public XidImpl() + { + + } + + /** + * Create a new XidImpl from an existing Xid. + *

Usefull for casting external Xids + * + * @param xid Foreign Xid. + */ + public XidImpl(Xid xid) + { + _branchQualifier = xid.getBranchQualifier(); + _formatID = xid.getFormatId(); + _globalTransactionID = xid.getGlobalTransactionId(); + } + + /** + * Create a new Xid. + * + * @param branchQualifier The transaction branch identifier part of XID as an array of bytes. + * @param format The format identifier part of the XID. + * @param globalTransactionID The global transaction identifier part of XID as an array of bytes. + */ + public XidImpl(byte[] branchQualifier, int format, byte[] globalTransactionID) + { + _branchQualifier = branchQualifier; + _formatID = format; + _globalTransactionID = globalTransactionID; + } + + /** + * Create a new Xid form its String form + * 4 1 1 g b + * +---+---+---+---+---+---+---+- -+---+---+- -+---+ + * | format_id | g | b | txn-id | br-id | + * +---+---+---+---+---+---+---+- -+---+---+- -+---+ + * 0 4 5 6 6+g 6+g+b + * format_id: an implementation specific format identifier + *

+ * gtrid_length: how many bytes of this form the transaction id + *

+ * bqual_length: how many bytes of this form the branch id + *

+ * data: a sequence of octets of at most 128 bytes containing the txn id and the + * branch id + *

+ * Note - The sum of the two lengths must equal the length of the data field. + * + * @param xid an XID STring Form + * @throws QpidException If the string does not represent a valid Xid + */ + public XidImpl(String xid) throws QpidException + { + if (_logger.isDebugEnabled()) + { + _logger.debug("converting string " + xid + " into XidImpl"); + } + try + { + DataInputStream input = new DataInputStream(new ByteArrayInputStream(xid.getBytes())); + _formatID = (int) input.readLong(); + int g = input.readByte(); + int b = input.readByte(); + _globalTransactionID = new byte[g]; + _branchQualifier = new byte[b]; + if (input.read(_globalTransactionID, 0, g) != g) + { + throw new QpidException("Cannot convert the string " + xid + " into an Xid", null, null); + } + if (input.read(_branchQualifier, 0, b) != b) + { + throw new QpidException("Cannot convert the string " + xid + " into an Xid", null, null); + } + } + catch (IOException e) + { + throw new QpidException("cannot convert the string " + xid + " into an Xid", null, e); + } + } + + //--- Xid interface implementation + + /** + * Format identifier. O means the OSI CCR format. + * + * @return Global transaction identifier. + */ + public byte[] getGlobalTransactionId() + { + return _globalTransactionID; + } + + /** + * Obtain the transaction branch identifier part of XID as an array of bytes. + * + * @return Branch identifier part of XID. + */ + public byte[] getBranchQualifier() + { + return _branchQualifier; + } + + /** + * Obtain the format identifier part of the XID. + * + * @return Format identifier. O means the OSI CCR format. + */ + public int getFormatId() + { + return _formatID; + } + + //--- Object operations + + /** + * Indicates whether some other Xid is "equal to" this one. + *

Two Xids are equal if and only if their three elementary parts are equal + * + * @param o the object to compare this XidImpl against. + * @return true if the XidImpl are equal, false otherwise. + */ + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o instanceof XidImpl) + { + XidImpl other = (XidImpl) o; + if (_formatID == other.getFormatId()) + { + if (_branchQualifier.length == other.getBranchQualifier().length) + { + for (int i = 0; i < _branchQualifier.length; i++) + { + if (_branchQualifier[i] != other.getBranchQualifier()[i]) + { + return false; + } + } + if (_globalTransactionID.length == other.getGlobalTransactionId().length) + { + for (int i = 0; i < _globalTransactionID.length; i++) + { + if (_globalTransactionID[i] != other.getGlobalTransactionId()[i]) + { + return false; + } + } + // everithing is equal + return true; + } + } + } + } + return false; + } + + //-- Static helper method + /** + * Convert an Xid into the AMQP String format. + * + * 4 1 1 g b + * +---+---+---+---+---+---+---+- -+---+---+- -+---+ + * | format_id | g | b | txn-id | br-id | + * +---+---+---+---+---+---+---+- -+---+---+- -+---+ + * 0 4 5 6 6+g 6+g+b + * format_id: an implementation specific format identifier + *

+ * gtrid_length: how many bytes of this form the transaction id + *

+ * bqual_length: how many bytes of this form the branch id + *

+ * data: a sequence of octets of at most 128 bytes containing the txn id and the + * branch id + *

+ * Note - The sum of the two lengths must equal the length of the data field. + * + * @param xid an Xid to convert. + * @return The String representation of this Xid + * @throws QpidException In case of problem when converting this Xid into a string. + */ + public static org.apache.qpid.transport.Xid convert(Xid xid) throws QpidException + { + return new org.apache.qpid.transport.Xid(xid.getFormatId(), + xid.getGlobalTransactionId(), + xid.getBranchQualifier()); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java b/RC5/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.java new file mode 100644 index 0000000000..123901b577 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/exchange/ExchangeDefaults.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.exchange; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Defines the names of the standard AMQP exchanges that every AMQP broker should provide. These exchange names + * and type are given in the specification. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Defines the standard AMQP exchange names. + *
Defines the standard AMQP exchange types. + *
+ * + * @todo A type safe enum, might be more appropriate for the exchange types. + */ +public class ExchangeDefaults +{ + /** The default direct exchange, which is a special internal exchange that cannot be explicitly bound to. */ + public static final AMQShortString DEFAULT_EXCHANGE_NAME = new AMQShortString("<>"); + + /** The pre-defined topic exchange, the broker SHOULD provide this. */ + public static final AMQShortString TOPIC_EXCHANGE_NAME = new AMQShortString("amq.topic"); + + /** Defines the identifying type name of topic exchanges. */ + public static final AMQShortString TOPIC_EXCHANGE_CLASS = new AMQShortString("topic"); + + /** The pre-defined direct exchange, the broker MUST provide this. */ + public static final AMQShortString DIRECT_EXCHANGE_NAME = new AMQShortString("amq.direct"); + + /** Defines the identifying type name of direct exchanges. */ + public static final AMQShortString DIRECT_EXCHANGE_CLASS = new AMQShortString("direct"); + + /** The pre-defined headers exchange, the specification does not say this needs to be provided. */ + public static final AMQShortString HEADERS_EXCHANGE_NAME = new AMQShortString("amq.match"); + + /** Defines the identifying type name of headers exchanges. */ + public static final AMQShortString HEADERS_EXCHANGE_CLASS = new AMQShortString("headers"); + + /** The pre-defined fanout exchange, the boker MUST provide this. */ + public static final AMQShortString FANOUT_EXCHANGE_NAME = new AMQShortString("amq.fanout"); + + /** Defines the identifying type name of fanout exchanges. */ + public static final AMQShortString FANOUT_EXCHANGE_CLASS = new AMQShortString("fanout"); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQBody.java new file mode 100644 index 0000000000..fe04155bb8 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQBody.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.AMQException; + +public interface AMQBody +{ + public byte getFrameType(); + + /** + * Get the size of the body + * @return unsigned short + */ + public abstract int getSize(); + + public void writePayload(ByteBuffer buffer); + + void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession) throws AMQException; +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.java new file mode 100644 index 0000000000..a2fc3a03ef --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlock.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.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * A data block represents something that has a size in bytes and the ability to write itself to a byte + * buffer (similar to a byte array). + */ +public abstract class AMQDataBlock implements EncodableAMQDataBlock +{ + /** + * Get the size of buffer needed to store the byte representation of this + * frame. + * @return unsigned integer + */ + public abstract long getSize(); + + /** + * Writes the datablock to the specified buffer. + * @param buffer + */ + public abstract void writePayload(ByteBuffer buffer); + + public ByteBuffer toByteBuffer() + { + final ByteBuffer buffer = ByteBuffer.allocate((int)getSize()); + + writePayload(buffer); + buffer.flip(); + return buffer; + } + + public java.nio.ByteBuffer toNioByteBuffer() + { + final java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate((int) getSize()); + + ByteBuffer buf = ByteBuffer.wrap(buffer); + writePayload(buf); + buffer.flip(); + return buffer; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java new file mode 100644 index 0000000000..82ffc60802 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockDecoder.java @@ -0,0 +1,120 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; + +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQDataBlockDecoder +{ + private static final String SESSION_METHOD_BODY_FACTORY = "QPID_SESSION_METHOD_BODY_FACTORY"; + + private static final BodyFactory[] _bodiesSupported = new BodyFactory[Byte.MAX_VALUE]; + + static + { + _bodiesSupported[ContentHeaderBody.TYPE] = ContentHeaderBodyFactory.getInstance(); + _bodiesSupported[ContentBody.TYPE] = ContentBodyFactory.getInstance(); + _bodiesSupported[HeartbeatBody.TYPE] = new HeartbeatBodyFactory(); + } + + Logger _logger = LoggerFactory.getLogger(AMQDataBlockDecoder.class); + + public AMQDataBlockDecoder() + { } + + public boolean decodable(IoSession session, ByteBuffer in) throws AMQFrameDecodingException + { + final int remainingAfterAttributes = in.remaining() - (1 + 2 + 4 + 1); + // type, channel, body length and end byte + if (remainingAfterAttributes < 0) + { + return false; + } + + in.skip(1 + 2); + final long bodySize = in.getUnsignedInt(); + + return (remainingAfterAttributes >= bodySize); + + } + + protected Object createAndPopulateFrame(IoSession session, ByteBuffer in) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + final byte type = in.get(); + + BodyFactory bodyFactory; + if (type == AMQMethodBody.TYPE) + { + bodyFactory = (BodyFactory) session.getAttribute(SESSION_METHOD_BODY_FACTORY); + if (bodyFactory == null) + { + AMQVersionAwareProtocolSession protocolSession = (AMQVersionAwareProtocolSession) session.getAttachment(); + bodyFactory = new AMQMethodBodyFactory(protocolSession); + session.setAttribute(SESSION_METHOD_BODY_FACTORY, bodyFactory); + + } + + } + else + { + bodyFactory = _bodiesSupported[type]; + } + + if (bodyFactory == null) + { + throw new AMQFrameDecodingException(null, "Unsupported frame type: " + type, null); + } + + final int channel = in.getUnsignedShort(); + final long bodySize = in.getUnsignedInt(); + + // bodySize can be zero + if ((channel < 0) || (bodySize < 0)) + { + throw new AMQFrameDecodingException(null, "Undecodable frame: type = " + type + " channel = " + channel + + " bodySize = " + bodySize, null); + } + + AMQFrame frame = new AMQFrame(in, channel, bodySize, bodyFactory); + + byte marker = in.get(); + if ((marker & 0xFF) != 0xCE) + { + throw new AMQFrameDecodingException(null, "End of frame marker not found. Read " + marker + " length=" + bodySize + + " type=" + type, null); + } + + return frame; + } + + public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception + { + out.write(createAndPopulateFrame(session, in)); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.java new file mode 100644 index 0000000000..05fd2bb480 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQDataBlockEncoder.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolEncoderOutput; +import org.apache.mina.filter.codec.demux.MessageEncoder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; + +public final class AMQDataBlockEncoder implements MessageEncoder +{ + private static final Logger _logger = LoggerFactory.getLogger(AMQDataBlockEncoder.class); + + private final Set _messageTypes = Collections.singleton(EncodableAMQDataBlock.class); + + public AMQDataBlockEncoder() + { } + + public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception + { + final AMQDataBlock frame = (AMQDataBlock) message; + + final ByteBuffer buffer = frame.toByteBuffer(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("Encoded frame byte-buffer is '" + EncodingUtils.convertToHexString(buffer) + "'"); + } + + out.write(buffer); + } + + public Set getMessageTypes() + { + return _messageTypes; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.java new file mode 100644 index 0000000000..02a46f3748 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrame.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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class AMQFrame extends AMQDataBlock implements EncodableAMQDataBlock +{ + private final int _channel; + + private final AMQBody _bodyFrame; + public static final byte FRAME_END_BYTE = (byte) 0xCE; + + + public AMQFrame(final int channel, final AMQBody bodyFrame) + { + _channel = channel; + _bodyFrame = bodyFrame; + } + + public AMQFrame(final ByteBuffer in, final int channel, final long bodySize, final BodyFactory bodyFactory) throws AMQFrameDecodingException + { + this._channel = channel; + this._bodyFrame = bodyFactory.createBody(in,bodySize); + } + + public long getSize() + { + return 1 + 2 + 4 + _bodyFrame.getSize() + 1; + } + + public static final int getFrameOverhead() + { + return 1 + 2 + 4 + 1; + } + + + public void writePayload(ByteBuffer buffer) + { + buffer.put(_bodyFrame.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, _channel); + EncodingUtils.writeUnsignedInteger(buffer, _bodyFrame.getSize()); + _bodyFrame.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + } + + public final int getChannel() + { + return _channel; + } + + public final AMQBody getBodyFrame() + { + return _bodyFrame; + } + + public String toString() + { + return "Frame channelId: " + _channel + ", bodyFrame: " + String.valueOf(_bodyFrame); + } + + public static void writeFrame(ByteBuffer buffer, final int channel, AMQBody body) + { + buffer.put(body.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body.getSize()); + body.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + + } + + public static void writeFrames(ByteBuffer buffer, final int channel, AMQBody body1, AMQBody body2) + { + buffer.put(body1.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body1.getSize()); + body1.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + buffer.put(body2.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body2.getSize()); + body2.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + + } + + public static void writeFrames(ByteBuffer buffer, final int channel, AMQBody body1, AMQBody body2, AMQBody body3) + { + buffer.put(body1.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body1.getSize()); + body1.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + buffer.put(body2.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body2.getSize()); + body2.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + buffer.put(body3.getFrameType()); + EncodingUtils.writeUnsignedShort(buffer, channel); + EncodingUtils.writeUnsignedInteger(buffer, body3.getSize()); + body3.writePayload(buffer); + buffer.put(FRAME_END_BYTE); + + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.java new file mode 100644 index 0000000000..2373edb478 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQFrameDecodingException.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.framing; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +/** + * AMQFrameDecodingException indicates that an AMQP frame cannot be decoded because it does not have the correct + * format as defined by the protocol. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represents a format error in a protocol frame. + *
+ */ +public class AMQFrameDecodingException extends AMQException +{ + public AMQFrameDecodingException(AMQConstant errorCode, String message, Throwable cause) + { + super(errorCode, message, cause); + } + + public AMQFrameDecodingException(AMQConstant errorCode, String message) + { + super(errorCode, message, null); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.java new file mode 100644 index 0000000000..4763b22290 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBody.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; + +public interface AMQMethodBody extends AMQBody +{ + public static final byte TYPE = 1; + + /** AMQP version */ + public byte getMajor(); + + public byte getMinor(); + + + + /** @return unsigned short */ + public int getClazz(); + + /** @return unsigned short */ + public int getMethod(); + + public void writeMethodPayload(ByteBuffer buffer); + + + public int getSize(); + + public void writePayload(ByteBuffer buffer); + + //public abstract void populateMethodBodyFromBuffer(ByteBuffer buffer) throws AMQFrameDecodingException; + + //public void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException; + + public AMQFrame generateFrame(int channelId); + + public String toString(); + + + + /** + * Convenience Method to create a channel not found exception + * + * @param channelId The channel id that is not found + * + * @return new AMQChannelException + */ + public AMQChannelException getChannelNotFoundException(int channelId); + + public AMQChannelException getChannelException(AMQConstant code, String message); + + public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause); + + public AMQConnectionException getConnectionException(AMQConstant code, String message); + + + public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause); + + + public boolean execute(MethodDispatcher methodDispatcher, int channelId) throws AMQException; +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.java new file mode 100644 index 0000000000..1a7022c11b --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AMQMethodBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private final AMQVersionAwareProtocolSession _protocolSession; + + public AMQMethodBodyFactory(AMQVersionAwareProtocolSession protocolSession) + { + _protocolSession = protocolSession; + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return _protocolSession.getMethodRegistry().convertToBody(in, bodySize); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java new file mode 100644 index 0000000000..ad7f36f790 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyImpl.java @@ -0,0 +1,96 @@ +package org.apache.qpid.framing; + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.AMQChannelException; +import org.apache.qpid.AMQConnectionException; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; + +public abstract class AMQMethodBodyImpl implements AMQMethodBody +{ + public static final byte TYPE = 1; + + public AMQMethodBodyImpl() + { + } + + public byte getFrameType() + { + return TYPE; + } + + + /** unsigned short */ + abstract protected int getBodySize(); + + + public AMQFrame generateFrame(int channelId) + { + return new AMQFrame(channelId, this); + } + + /** + * Creates an AMQChannelException for the corresponding body type (a channel exception should include the class and + * method ids of the body it resulted from). + */ + + /** + * Convenience Method to create a channel not found exception + * + * @param channelId The channel id that is not found + * + * @return new AMQChannelException + */ + public AMQChannelException getChannelNotFoundException(int channelId) + { + return getChannelException(AMQConstant.NOT_FOUND, "Channel not found for id:" + channelId); + } + + public AMQChannelException getChannelException(AMQConstant code, String message) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQChannelException getChannelException(AMQConstant code, String message, Throwable cause) + { + return new AMQChannelException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), null); + } + + public AMQConnectionException getConnectionException(AMQConstant code, String message, Throwable cause) + { + return new AMQConnectionException(code, message, getClazz(), getMethod(), getMajor(), getMinor(), cause); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) throws AMQException + { + session.methodFrameReceived(channelId, this); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.java new file mode 100644 index 0000000000..0c61d9db3c --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodBodyInstanceFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + + +public abstract interface AMQMethodBodyInstanceFactory +{ + public AMQMethodBody newInstance(ByteBuffer buffer, long size) throws AMQFrameDecodingException; +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.java new file mode 100644 index 0000000000..bfcc38ad60 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQMethodFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + + +public interface AMQMethodFactory +{ + + // Connection Methods + + ConnectionCloseBody createConnectionClose(); + + // Access Methods + + AccessRequestBody createAccessRequest(boolean active, boolean exclusive, boolean passive, boolean read, AMQShortString realm, boolean write); + + + // Tx Methods + + TxSelectBody createTxSelect(); + + TxCommitBody createTxCommit(); + + TxRollbackBody createTxRollback(); + + // Channel Methods + + ChannelOpenBody createChannelOpen(); + + ChannelCloseBody createChannelClose(int replyCode, AMQShortString replyText); + + ChannelFlowBody createChannelFlow(boolean active); + + + // Exchange Methods + + + ExchangeBoundBody createExchangeBound(AMQShortString exchangeName, + AMQShortString queueName, + AMQShortString routingKey); + + ExchangeDeclareBody createExchangeDeclare(AMQShortString name, AMQShortString type, int ticket); + + + // Queue Methods + + QueueDeclareBody createQueueDeclare(AMQShortString name, FieldTable arguments, boolean autoDelete, boolean durable, boolean exclusive, boolean passive, int ticket); + + QueueBindBody createQueueBind(AMQShortString queueName, AMQShortString exchangeName, AMQShortString routingKey, FieldTable arguments, int ticket); + + QueueDeleteBody createQueueDelete(AMQShortString queueName, boolean ifEmpty, boolean ifUnused, int ticket); + + + // Message Methods + + // In different versions of the protocol we change the class used for message transfer + // abstract this out so the appropriate methods are created + AMQMethodBody createRecover(boolean requeue); + + AMQMethodBody createConsumer(AMQShortString tag, AMQShortString queueName, FieldTable arguments, boolean noAck, boolean exclusive, boolean noLocal, int ticket); + + AMQMethodBody createConsumerCancel(AMQShortString consumerTag); + + AMQMethodBody createAcknowledge(long deliveryTag, boolean multiple); + + AMQMethodBody createRejectBody(long deliveryTag, boolean requeue); + + AMQMethodBody createMessageQos(int prefetchCount, int prefetchSize); + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.java new file mode 100644 index 0000000000..ab09c1de6d --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolClassException.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.framing; + +/** + * AMQProtocolInstanceException indicates that the protocol class is incorrect in a header. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent incorrect protocol class in frame header. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolClassException extends AMQProtocolHeaderException +{ + public AMQProtocolClassException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.java new file mode 100644 index 0000000000..6b819364da --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolHeaderException.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.framing; + +import org.apache.qpid.AMQException; + +/** + * AMQProtocolHeaderException indicates a format error in an AMQP frame header. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent format error in frame header. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolHeaderException extends AMQException +{ + public AMQProtocolHeaderException(String message, Throwable cause) + { + super(null, message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.java new file mode 100644 index 0000000000..3165c373a9 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolInstanceException.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.framing; + +/** + * AMQProtocolInstanceException indicates that the protocol instance is incorrect in a header. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent incorrect protocol instance in frame header. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolInstanceException extends AMQProtocolHeaderException +{ + public AMQProtocolInstanceException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.java new file mode 100644 index 0000000000..c9b0973ea6 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQProtocolVersionException.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.framing; + +/** + * AMQProtocolInstanceException indicates that the client and server differ on expected protocol version in a header. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent incorrect protocol version in frame header. + *
+ * + * @todo Not an AMQP exception as no status code. + */ +public class AMQProtocolVersionException extends AMQProtocolHeaderException +{ + public AMQProtocolVersionException(String message, Throwable cause) + { + super(message, cause); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java new file mode 100644 index 0000000000..a8e7f47db0 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java @@ -0,0 +1,776 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.lang.ref.WeakReference; + +/** + * A short string is a representation of an AMQ Short String + * Short strings differ from the Java String class by being limited to on ASCII characters (0-127) + * and thus can be held more effectively in a byte buffer. + * + */ +public final class AMQShortString implements CharSequence, Comparable +{ + private static final byte MINUS = (byte)'-'; + private static final byte ZERO = (byte) '0'; + + + + private final class TokenizerImpl implements AMQShortStringTokenizer + { + private final byte _delim; + private int _count = -1; + private int _pos = 0; + + public TokenizerImpl(final byte delim) + { + _delim = delim; + } + + public int countTokens() + { + if(_count == -1) + { + _count = 1 + AMQShortString.this.occurences(_delim); + } + return _count; + } + + public AMQShortString nextToken() + { + if(_pos <= AMQShortString.this.length()) + { + int nextDelim = AMQShortString.this.indexOf(_delim, _pos); + if(nextDelim == -1) + { + nextDelim = AMQShortString.this.length(); + } + + AMQShortString nextToken = AMQShortString.this.substring(_pos, nextDelim++); + _pos = nextDelim; + return nextToken; + } + else + { + return null; + } + } + + public boolean hasMoreTokens() + { + return _pos <= AMQShortString.this.length(); + } + } + + private AMQShortString substring(final int from, final int to) + { + return new AMQShortString(_data, from+_offset, to+_offset); + } + + + private static final ThreadLocal>> _localInternMap = + new ThreadLocal>>() + { + protected Map> initialValue() + { + return new WeakHashMap>(); + }; + }; + + private static final Map> _globalInternMap = + new WeakHashMap>(); + + private static final Logger _logger = LoggerFactory.getLogger(AMQShortString.class); + + private final byte[] _data; + private final int _offset; + private int _hashCode; + private String _asString = null; + + private final int _length; + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + + public static final AMQShortString EMPTY_STRING = new AMQShortString((String)null); + + public AMQShortString(byte[] data) + { + + _data = data.clone(); + _length = data.length; + _offset = 0; + } + + public AMQShortString(byte[] data, int pos) + { + final int size = data[pos++]; + final byte[] dataCopy = new byte[size]; + System.arraycopy(data,pos,dataCopy,0,size); + _length = size; + _data = dataCopy; + _offset = 0; + } + + public AMQShortString(String data) + { + this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray()); + _asString = data; + } + + public AMQShortString(char[] data) + { + if (data == null) + { + throw new NullPointerException("Cannot create AMQShortString with null char[]"); + } + + final int length = data.length; + final byte[] stringBytes = new byte[length]; + int hash = 0; + for (int i = 0; i < length; i++) + { + stringBytes[i] = (byte) (0xFF & data[i]); + hash = (31 * hash) + stringBytes[i]; + } + _hashCode = hash; + _data = stringBytes; + + _length = length; + _offset = 0; + + } + + public AMQShortString(CharSequence charSequence) + { + final int length = charSequence.length(); + final byte[] stringBytes = new byte[length]; + int hash = 0; + for (int i = 0; i < length; i++) + { + stringBytes[i] = ((byte) (0xFF & charSequence.charAt(i))); + hash = (31 * hash) + stringBytes[i]; + + } + + _data = stringBytes; + _hashCode = hash; + _length = length; + _offset = 0; + + } + + private AMQShortString(ByteBuffer data, final int length) + { + if(data.isDirect() || data.isReadOnly()) + { + byte[] dataBytes = new byte[length]; + data.get(dataBytes); + _data = dataBytes; + _offset = 0; + } + else + { + + _data = data.array(); + _offset = data.arrayOffset() + data.position(); + data.skip(length); + + } + _length = length; + + } + + private AMQShortString(final byte[] data, final int from, final int to) + { + _offset = from; + _length = to - from; + _data = data; + } + + public AMQShortString shrink() + { + if(_data.length != _length) + { + byte[] dataBytes = new byte[_length]; + System.arraycopy(_data,_offset,dataBytes,0,_length); + return new AMQShortString(dataBytes,0,_length); + } + else + { + return this; + } + } + + /** + * Get the length of the short string + * @return length of the underlying byte array + */ + public int length() + { + return _length; + } + + public char charAt(int index) + { + + return (char) _data[_offset + index]; + + } + + public CharSequence subSequence(int start, int end) + { + return new CharSubSequence(start, end); + } + + public int writeToByteArray(byte[] encoding, int pos) + { + final int size = length(); + encoding[pos++] = (byte) size; + System.arraycopy(_data,_offset,encoding,pos,size); + return pos+size; + } + + public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos) + { + + + final AMQShortString shortString = new AMQShortString(byteEncodedDestination, pos); + if(shortString.length() == 0) + { + return null; + } + else + { + return shortString; + } + } + + public static AMQShortString readFromBuffer(ByteBuffer buffer) + { + final short length = buffer.getUnsigned(); + if (length == 0) + { + return null; + } + else + { + + return new AMQShortString(buffer, length); + } + } + + public byte[] getBytes() + { + if(_offset == 0 && _length == _data.length) + { + return _data.clone(); + } + else + { + byte[] data = new byte[_length]; + System.arraycopy(_data,_offset,data,0,_length); + return data; + } + } + + public void writeToBuffer(ByteBuffer buffer) + { + + final int size = length(); + //buffer.setAutoExpand(true); + buffer.put((byte) size); + buffer.put(_data, _offset, size); + + } + + public boolean endsWith(String s) + { + return endsWith(new AMQShortString(s)); + } + + + public boolean endsWith(AMQShortString otherString) + { + + if (otherString.length() > length()) + { + return false; + } + + + int thisLength = length(); + int otherLength = otherString.length(); + + for (int i = 1; i <= otherLength; i++) + { + if (charAt(thisLength - i) != otherString.charAt(otherLength - i)) + { + return false; + } + } + return true; + } + + public boolean startsWith(String s) + { + return startsWith(new AMQShortString(s)); + } + + public boolean startsWith(AMQShortString otherString) + { + + if (otherString.length() > length()) + { + return false; + } + + for (int i = 0; i < otherString.length(); i++) + { + if (charAt(i) != otherString.charAt(i)) + { + return false; + } + } + + return true; + + } + + public boolean startsWith(CharSequence otherString) + { + if (otherString.length() > length()) + { + return false; + } + + for (int i = 0; i < otherString.length(); i++) + { + if (charAt(i) != otherString.charAt(i)) + { + return false; + } + } + + return true; + } + + + private final class CharSubSequence implements CharSequence + { + private final int _sequenceOffset; + private final int _end; + + public CharSubSequence(final int offset, final int end) + { + _sequenceOffset = offset; + _end = end; + } + + public int length() + { + return _end - _sequenceOffset; + } + + public char charAt(int index) + { + return AMQShortString.this.charAt(index + _sequenceOffset); + } + + public CharSequence subSequence(int start, int end) + { + return new CharSubSequence(start + _sequenceOffset, end + _sequenceOffset); + } + } + + public char[] asChars() + { + final int size = length(); + final char[] chars = new char[size]; + + for (int i = 0; i < size; i++) + { + chars[i] = (char) _data[i + _offset]; + } + + return chars; + } + + + public String asString() + { + if (_asString == null) + { + _asString = new String(asChars()); + } + return _asString; + } + + public boolean equals(Object o) + { + + + if(o instanceof AMQShortString) + { + return equals((AMQShortString)o); + } + if(o instanceof CharSequence) + { + return equals((CharSequence)o); + } + + if (o == null) + { + return false; + } + + if (o == this) + { + return true; + } + + + return false; + + } + + public boolean equals(final AMQShortString otherString) + { + if (otherString == this) + { + return true; + } + + if (otherString == null) + { + return false; + } + + final int hashCode = _hashCode; + + final int otherHashCode = otherString._hashCode; + + if ((hashCode != 0) && (otherHashCode != 0) && (hashCode != otherHashCode)) + { + return false; + } + + final int length = _length; + + if(length != otherString._length) + { + return false; + } + + + final byte[] data = _data; + + final byte[] otherData = otherString._data; + + final int offset = _offset; + + final int otherOffset = otherString._offset; + + if(offset == 0 && otherOffset == 0 && length == data.length && length == otherData.length) + { + return Arrays.equals(data, otherData); + } + else + { + int thisIdx = offset; + int otherIdx = otherOffset; + for(int i = length; i-- != 0; ) + { + if(!(data[thisIdx++] == otherData[otherIdx++])) + { + return false; + } + } + } + + return true; + + } + + public boolean equals(CharSequence s) + { + if(s instanceof AMQShortString) + { + return equals((AMQShortString)s); + } + + if (s == null) + { + return false; + } + + if (s.length() != length()) + { + return false; + } + + for (int i = 0; i < length(); i++) + { + if (charAt(i) != s.charAt(i)) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + int hash = _hashCode; + if (hash == 0) + { + final int size = length(); + + for (int i = 0; i < size; i++) + { + hash = (31 * hash) + _data[i+_offset]; + } + + _hashCode = hash; + } + + return hash; + } + + public void setDirty() + { + _hashCode = 0; + } + + public String toString() + { + return asString(); + } + + public int compareTo(AMQShortString name) + { + if (name == null) + { + return 1; + } + else + { + + if (name.length() < length()) + { + return -name.compareTo(this); + } + + for (int i = 0; i < length(); i++) + { + final byte d = _data[i+_offset]; + final byte n = name._data[i+name._offset]; + if (d < n) + { + return -1; + } + + if (d > n) + { + return 1; + } + } + + return (length() == name.length()) ? 0 : -1; + } + } + + + public AMQShortStringTokenizer tokenize(byte delim) + { + return new TokenizerImpl(delim); + } + + + public AMQShortString intern() + { + + hashCode(); + + Map> localMap = + _localInternMap.get(); + + WeakReference ref = localMap.get(this); + AMQShortString internString; + + if(ref != null) + { + internString = ref.get(); + if(internString != null) + { + return internString; + } + } + + + synchronized(_globalInternMap) + { + + ref = _globalInternMap.get(this); + if((ref == null) || ((internString = ref.get()) == null)) + { + internString = shrink(); + ref = new WeakReference(internString); + _globalInternMap.put(internString, ref); + } + + } + localMap.put(internString, ref); + return internString; + + } + + private int occurences(final byte delim) + { + int count = 0; + final int end = _offset + _length; + for(int i = _offset ; i < end ; i++ ) + { + if(_data[i] == delim) + { + count++; + } + } + return count; + } + + private int indexOf(final byte val, final int pos) + { + + for(int i = pos; i < length(); i++) + { + if(_data[_offset+i] == val) + { + return i; + } + } + return -1; + } + + + public static AMQShortString join(final Collection terms, + final AMQShortString delim) + { + if(terms.size() == 0) + { + return EMPTY_STRING; + } + + int size = delim.length() * (terms.size() - 1); + for(AMQShortString term : terms) + { + size += term.length(); + } + + byte[] data = new byte[size]; + int pos = 0; + final byte[] delimData = delim._data; + final int delimOffset = delim._offset; + final int delimLength = delim._length; + + + for(AMQShortString term : terms) + { + + if(pos!=0) + { + System.arraycopy(delimData, delimOffset,data,pos, delimLength); + pos+=delimLength; + } + System.arraycopy(term._data,term._offset,data,pos,term._length); + pos+=term._length; + } + + + + return new AMQShortString(data,0,size); + } + + public int toIntValue() + { + int pos = _offset; + int val = 0; + + + boolean isNegative = (_data[pos] == MINUS); + if(isNegative) + { + pos++; + } + + final int end = _length + _offset; + + while(pos < end) + { + int digit = (int) (_data[pos++] - ZERO); + if((digit < 0) || (digit > 9)) + { + throw new NumberFormatException("\""+toString()+"\" is not a valid number"); + } + val = val * 10; + val += digit; + } + if(isNegative) + { + val = val * -1; + } + return val; + } + + public boolean contains(final byte b) + { + final int end = _length + _offset; + for(int i = _offset; i < end; i++) + { + if(_data[i] == b) + { + return true; + } + } + return false; //To change body of created methods use File | Settings | File Templates. + } + + + public static void main(String args[]) + { + AMQShortString s = new AMQShortString("a.b.c.d.e.f.g.h.i.j.k"); + AMQShortString s2 = s.substring(2, 7); + + AMQShortStringTokenizer t = s2.tokenize((byte) '.'); + while(t.hasMoreTokens()) + { + System.err.println(t.nextToken()); + } + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java new file mode 100644 index 0000000000..e2db8906a1 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQShortStringTokenizer.java @@ -0,0 +1,31 @@ +package org.apache.qpid.framing; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +public interface AMQShortStringTokenizer +{ + + public int countTokens(); + + public AMQShortString nextToken(); + + boolean hasMoreTokens(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQType.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQType.java new file mode 100644 index 0000000000..14fb63da03 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQType.java @@ -0,0 +1,795 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import java.math.BigDecimal; + +/** + * AMQType is a type that represents the different possible AMQP field table types. It provides operations for each + * of the types to perform tasks such as calculating the size of an instance of the type, converting types between AMQP + * and Java native types, and reading and writing instances of AMQP types in binary formats to and from byte buffers. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Get the equivalent one byte identifier for a type. + *
Calculate the size of an instance of an AMQP parameter type. {@link EncodingUtils} + *
Convert an instance of an AMQP parameter into a compatable Java object tagged with its AMQP type. + * {@link AMQTypedValue} + *
Write an instance of an AMQP parameter type to a byte buffer. {@link EncodingUtils} + *
Read an instance of an AMQP parameter from a byte buffer. {@link EncodingUtils} + *
+ */ +public enum AMQType +{ + LONG_STRING('S') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + INTEGER('i') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.unsignedIntegerLength(); + } + + public Long toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeUnsignedInteger(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readUnsignedInteger(buffer); + } + }, + + DECIMAL('D') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength() + EncodingUtils.encodedIntegerLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to BigDecimal."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + BigDecimal bd = (BigDecimal) value; + + byte places = new Integer(bd.scale()).byteValue(); + + int unscaled = bd.intValue(); + + EncodingUtils.writeByte(buffer, places); + + EncodingUtils.writeInteger(buffer, unscaled); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + byte places = EncodingUtils.readByte(buffer); + + int unscaled = EncodingUtils.readInteger(buffer); + + BigDecimal bd = new BigDecimal(unscaled); + + return bd.setScale(places); + } + }, + + TIMESTAMP('T') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to timestamp."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + /** + * Implements the field table type. The native value of a field table type will be an instance of + * {@link FieldTable}, which itself may contain name/value pairs encoded as {@link AMQTypedValue}s. + */ + FIELD_TABLE('F') + { + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public int getEncodingSize(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/value pairs adding up size of each. FieldTable itself keeps track of its encoded + // size as entries are added, so no need to loop over all explicitly. + // EncodingUtils calculation of the encoded field table lenth, will include 4 bytes for its 'size' field. + return EncodingUtils.encodedFieldTableLength(ftValue); + } + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public Object toNativeValue(Object value) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + return (FieldTable) value; + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeValueImpl(Object value, ByteBuffer buffer) + { + // Ensure that the value is a FieldTable. + if (!(value instanceof FieldTable)) + { + throw new IllegalArgumentException("Value is not a FieldTable."); + } + + FieldTable ftValue = (FieldTable) value; + + // Loop over all name/values writing out into buffer. + ftValue.writeToBuffer(buffer); + } + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + public Object readValueFromBuffer(ByteBuffer buffer) + { + try + { + // Read size of field table then all name/value pairs. + return EncodingUtils.readFieldTable(buffer); + } + catch (AMQFrameDecodingException e) + { + throw new IllegalArgumentException("Unable to read field table from buffer.", e); + } + } + }, + + VOID('V') + { + public int getEncodingSize(Object value) + { + return 0; + } + + public Object toNativeValue(Object value) + { + if (value == null) + { + return null; + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to null String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return null; + } + }, + + BINARY('x') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongstrLength((byte[]) value); + } + + public Object toNativeValue(Object value) + { + if ((value instanceof byte[]) || (value == null)) + { + return value; + } + else + { + throw new IllegalArgumentException("Value: " + value + " (" + value.getClass().getName() + + ") cannot be converted to byte[]"); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongstr(buffer, (byte[]) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongstr(buffer); + } + }, + + ASCII_STRING('c') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + WIDE_STRING('C') + { + public int getEncodingSize(Object value) + { + // FIXME: use proper charset encoder + return EncodingUtils.encodedLongStringLength((String) value); + } + + public String toNativeValue(Object value) + { + if (value != null) + { + return value.toString(); + } + else + { + throw new NullPointerException("Cannot convert: null to String."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLongStringBytes(buffer, (String) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLongString(buffer); + } + }, + + BOOLEAN('t') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedBooleanLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Boolean) + { + return (Boolean) value; + } + else if ((value instanceof String) || (value == null)) + { + return Boolean.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to boolean."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeBoolean(buffer, (Boolean) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readBoolean(buffer); + } + }, + + ASCII_CHARACTER('k') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedCharLength(); + } + + public Character toNativeValue(Object value) + { + if (value instanceof Character) + { + return (Character) value; + } + else if (value == null) + { + throw new NullPointerException("Cannot convert null into char"); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to char."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeChar(buffer, (Character) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readChar(buffer); + } + }, + + BYTE('b') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedByteLength(); + } + + public Byte toNativeValue(Object value) + { + if (value instanceof Byte) + { + return (Byte) value; + } + else if ((value instanceof String) || (value == null)) + { + return Byte.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to byte."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeByte(buffer, (Byte) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readByte(buffer); + } + }, + + SHORT('s') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedShortLength(); + } + + public Short toNativeValue(Object value) + { + if (value instanceof Short) + { + return (Short) value; + } + else if (value instanceof Byte) + { + return ((Byte) value).shortValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Short.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to short."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeShort(buffer, (Short) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + }, + + INT('I') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedIntegerLength(); + } + + public Integer toNativeValue(Object value) + { + if (value instanceof Integer) + { + return (Integer) value; + } + 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); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + ") to int."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeInteger(buffer, (Integer) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readInteger(buffer); + } + }, + + LONG('l') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedLongLength(); + } + + public Object toNativeValue(Object value) + { + if (value instanceof Long) + { + return (Long) value; + } + else if (value instanceof Integer) + { + return ((Integer) value).longValue(); + } + else if (value instanceof Short) + { + return ((Short) value).longValue(); + } + else if (value instanceof Byte) + { + return ((Byte) value).longValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Long.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to long."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeLong(buffer, (Long) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readLong(buffer); + } + }, + + FLOAT('f') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedFloatLength(); + } + + public Float toNativeValue(Object value) + { + if (value instanceof Float) + { + return (Float) value; + } + else if ((value instanceof String) || (value == null)) + { + return Float.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to float."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeFloat(buffer, (Float) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readFloat(buffer); + } + }, + + DOUBLE('d') + { + public int getEncodingSize(Object value) + { + return EncodingUtils.encodedDoubleLength(); + } + + public Double toNativeValue(Object value) + { + if (value instanceof Double) + { + return (Double) value; + } + else if (value instanceof Float) + { + return ((Float) value).doubleValue(); + } + else if ((value instanceof String) || (value == null)) + { + return Double.valueOf((String) value); + } + else + { + throw new NumberFormatException("Cannot convert: " + value + "(" + value.getClass().getName() + + ") to double."); + } + } + + public void writeValueImpl(Object value, ByteBuffer buffer) + { + EncodingUtils.writeDouble(buffer, (Double) value); + } + + public Object readValueFromBuffer(ByteBuffer buffer) + { + return EncodingUtils.readDouble(buffer); + } + }; + + /** Holds the defined one byte identifier for the type. */ + private final byte _identifier; + + /** + * Creates an instance of an AMQP type from its defined one byte identifier. + * + * @param identifier The one byte identifier for the type. + */ + AMQType(char identifier) + { + _identifier = (byte) identifier; + } + + /** + * Extracts the byte identifier for the typ. + * + * @return The byte identifier for the typ. + */ + public final byte identifier() + { + return _identifier; + } + + /** + * Calculates the size of an instance of the type in bytes. + * + * @param value An instance of the type. + * + * @return The size of the instance of the type in bytes. + */ + public abstract int getEncodingSize(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation. + */ + public abstract Object toNativeValue(Object value); + + /** + * Converts an instance of the type to an equivalent Java native representation, packaged as an + * {@link AMQTypedValue} tagged with its AMQP type. + * + * @param value An instance of the type. + * + * @return An equivalent Java native representation, tagged with its AMQP type. + */ + public AMQTypedValue asTypedValue(Object value) + { + return new AMQTypedValue(this, toNativeValue(value)); + } + + /** + * Writes an instance of the type to a specified byte buffer, preceded by its one byte identifier. As the type and + * value are both written, this provides a fully encoded description of a parameters type and value. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + public void writeToBuffer(Object value, ByteBuffer buffer) + { + buffer.put(identifier()); + writeValueImpl(value, buffer); + } + + /** + * Writes an instance of the type to a specified byte buffer. + * + * @param value An instance of the type. + * @param buffer The byte buffer to write it to. + */ + abstract void writeValueImpl(Object value, ByteBuffer buffer); + + /** + * Reads an instance of the type from a specified byte buffer. + * + * @param buffer The byte buffer to write it to. + * + * @return An instance of the type. + */ + abstract Object readValueFromBuffer(ByteBuffer buffer); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.java new file mode 100644 index 0000000000..a16e137466 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypeMap.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.framing; + +import java.util.HashMap; +import java.util.Map; + +public class AMQTypeMap +{ + public static Map _reverseTypeMap = new HashMap(); + + static + { + for(AMQType type : AMQType.values()) + { + _reverseTypeMap.put(type.identifier(), type); + } + } + + public static AMQType getType(Byte identifier) + { + AMQType result = _reverseTypeMap.get(identifier); + if (result == null) { + throw new IllegalArgumentException + ("no such type code: " + Integer.toHexString(identifier.intValue())); + } + return result; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java new file mode 100644 index 0000000000..1ff39ca790 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.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.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * AMQTypedValue combines together a native Java Object value, and an {@link AMQType}, as a fully typed AMQP parameter + * value. It provides the ability to read and write fully typed parameters to and from byte buffers. It also provides + * the ability to create such parameters from Java native value and a type tag or to extract the native value and type + * from one. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Create a fully typed AMQP value from a native type and a type tag. {@link AMQType} + *
Create a fully typed AMQP value from a binary representation in a byte buffer. {@link AMQType} + *
Write a fully typed AMQP value to a binary representation in a byte buffer. {@link AMQType} + *
Extract the type from a fully typed AMQP value. + *
Extract the value from a fully typed AMQP value. + *
+ */ +public class AMQTypedValue +{ + /** The type of the value. */ + private final AMQType _type; + + /** The Java native representation of the AMQP typed value. */ + private final Object _value; + + public AMQTypedValue(AMQType type, Object value) + { + if (type == null) + { + throw new NullPointerException("Cannot create a typed value with null type"); + } + + _type = type; + _value = type.toNativeValue(value); + } + + private AMQTypedValue(AMQType type, ByteBuffer buffer) + { + _type = type; + _value = type.readValueFromBuffer(buffer); + } + + public AMQType getType() + { + return _type; + } + + public Object getValue() + { + return _value; + } + + public void writeToBuffer(ByteBuffer buffer) + { + _type.writeToBuffer(_value, buffer); + } + + public int getEncodingSize() + { + return _type.getEncodingSize(_value); + } + + public static AMQTypedValue readFromBuffer(ByteBuffer buffer) + { + AMQType type = AMQTypeMap.getType(buffer.get()); + + return new AMQTypedValue(type, buffer); + } + + public String toString() + { + return "[" + getType() + ": " + getValue() + "]"; + } + + + public boolean equals(Object o) + { + if(o instanceof AMQTypedValue) + { + AMQTypedValue other = (AMQTypedValue) o; + return _type == other._type && (_value == null ? other._value == null : _value.equals(other._value)); + } + else + { + return false; + } + } + + public int hashCode() + { + return _type.hashCode() ^ (_value == null ? 0 : _value.hashCode()); + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java new file mode 100644 index 0000000000..47b5c02beb --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/BasicContentHeaderProperties.java @@ -0,0 +1,834 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BasicContentHeaderProperties implements CommonContentHeaderProperties +{ + private static final Logger _logger = LoggerFactory.getLogger(BasicContentHeaderProperties.class); + + private static final AMQShortString ZERO_STRING = null; + + /** + * We store the encoded form when we decode the content header so that if we need to write it out without modifying + * it we can do so without incurring the expense of reencoding it + */ + private byte[] _encodedForm; + + /** Flag indicating whether the entire content header has been decoded yet */ + private boolean _decoded = true; + + /** + * We have some optimisations for partial decoding for maximum performance. The headers are used in the broker for + * routing in some cases so we can decode that separately. + */ + private boolean _decodedHeaders = true; + + /** + * We have some optimisations for partial decoding for maximum performance. The content type is used by all clients + * to determine the message type + */ + private boolean _decodedContentType = true; + + private AMQShortString _contentType; + + private AMQShortString _encoding; + + private FieldTable _headers; + + private byte _deliveryMode; + + private byte _priority; + + private AMQShortString _correlationId; + + private AMQShortString _replyTo; + + private long _expiration; + + private AMQShortString _messageId; + + private long _timestamp; + + private AMQShortString _type; + + private AMQShortString _userId; + + private AMQShortString _appId; + + private AMQShortString _clusterId; + + private int _propertyFlags = 0; + private static final int CONTENT_TYPE_MASK = 1 << 15; + private static final int ENCONDING_MASK = 1 << 14; + private static final int HEADERS_MASK = 1 << 13; + private static final int DELIVERY_MODE_MASK = 1 << 12; + private static final int PROPRITY_MASK = 1 << 11; + private static final int CORRELATION_ID_MASK = 1 << 10; + private static final int REPLY_TO_MASK = 1 << 9; + private static final int EXPIRATION_MASK = 1 << 8; + private static final int MESSAGE_ID_MASK = 1 << 7; + private static final int TIMESTAMP_MASK = 1 << 6; + private static final int TYPE_MASK = 1 << 5; + private static final int USER_ID_MASK = 1 << 4; + private static final int APPLICATION_ID_MASK = 1 << 3; + private static final int CLUSTER_ID_MASK = 1 << 2; + + + /** + * This is 0_10 specific. We use this property to check if some message properties have been changed. + */ + private boolean _hasBeenUpdated = false; + + public boolean reset() + { + boolean result = _hasBeenUpdated; + _hasBeenUpdated = false; + return result; + } + + public void updated() + { + _hasBeenUpdated = true; + } + + public BasicContentHeaderProperties() + { } + + public int getPropertyListSize() + { + if (_encodedForm != null) + { + return _encodedForm.length; + } + else + { + int size = 0; + + if ((_propertyFlags & (CONTENT_TYPE_MASK)) > 0) + { + size += EncodingUtils.encodedShortStringLength(_contentType); + } + + if ((_propertyFlags & ENCONDING_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_encoding); + } + + if ((_propertyFlags & HEADERS_MASK) > 0) + { + size += EncodingUtils.encodedFieldTableLength(_headers); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) > 0) + { + size += 1; + } + + if ((_propertyFlags & PROPRITY_MASK) > 0) + { + size += 1; + } + + if ((_propertyFlags & CORRELATION_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_correlationId); + } + + if ((_propertyFlags & REPLY_TO_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_replyTo); + } + + if ((_propertyFlags & EXPIRATION_MASK) > 0) + { + if (_expiration == 0L) + { + size += EncodingUtils.encodedShortStringLength(ZERO_STRING); + } + else + { + size += EncodingUtils.encodedShortStringLength(_expiration); + } + } + + if ((_propertyFlags & MESSAGE_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_messageId); + } + + if ((_propertyFlags & TIMESTAMP_MASK) > 0) + { + size += 8; + } + + if ((_propertyFlags & TYPE_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_type); + } + + if ((_propertyFlags & USER_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_userId); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_appId); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) > 0) + { + size += EncodingUtils.encodedShortStringLength(_clusterId); + } + + return size; + } + } + + private void clearEncodedForm() + { + if (!_decoded && (_encodedForm != null)) + { + // decode(); + } + + _encodedForm = null; + } + + public void setPropertyFlags(int propertyFlags) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags = propertyFlags; + } + + public int getPropertyFlags() + { + return _propertyFlags; + } + + public void writePropertyListPayload(ByteBuffer buffer) + { + if (_encodedForm != null) + { + buffer.put(_encodedForm); + } + else + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _contentType); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _encoding); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + EncodingUtils.writeFieldTableBytes(buffer, _headers); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) != 0) + { + buffer.put(_deliveryMode); + } + + if ((_propertyFlags & PROPRITY_MASK) != 0) + { + buffer.put(_priority); + } + + if ((_propertyFlags & CORRELATION_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _correlationId); + } + + if ((_propertyFlags & REPLY_TO_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _replyTo); + } + + if ((_propertyFlags & EXPIRATION_MASK) != 0) + { + if (_expiration == 0L) + { + EncodingUtils.writeShortStringBytes(buffer, ZERO_STRING); + } + else + { + EncodingUtils.writeShortStringBytes(buffer, String.valueOf(_expiration)); + } + } + + if ((_propertyFlags & MESSAGE_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _messageId); + } + + if ((_propertyFlags & TIMESTAMP_MASK) != 0) + { + EncodingUtils.writeTimestamp(buffer, _timestamp); + } + + if ((_propertyFlags & TYPE_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _type); + } + + if ((_propertyFlags & USER_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _userId); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _appId); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) != 0) + { + EncodingUtils.writeShortStringBytes(buffer, _clusterId); + } + } + } + + public void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size) throws AMQFrameDecodingException + { + _propertyFlags = propertyFlags; + + if (_logger.isDebugEnabled()) + { + _logger.debug("Property flags: " + _propertyFlags); + } + + decode(buffer); + /*_encodedForm = new byte[size]; + buffer.get(_encodedForm, 0, size); + _decoded = false; + _decodedHeaders = false; + _decodedContentType = false;*/ + } + + private void decode(ByteBuffer buffer) + { + // ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + int pos = buffer.position(); + try + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + _contentType = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + _encoding = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + _headers = EncodingUtils.readFieldTable(buffer); + } + + if ((_propertyFlags & DELIVERY_MODE_MASK) != 0) + { + _deliveryMode = buffer.get(); + } + + if ((_propertyFlags & PROPRITY_MASK) != 0) + { + _priority = buffer.get(); + } + + if ((_propertyFlags & CORRELATION_ID_MASK) != 0) + { + _correlationId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & REPLY_TO_MASK) != 0) + { + _replyTo = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & EXPIRATION_MASK) != 0) + { + _expiration = EncodingUtils.readLongAsShortString(buffer); + } + + if ((_propertyFlags & MESSAGE_ID_MASK) != 0) + { + _messageId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & TIMESTAMP_MASK) != 0) + { + _timestamp = EncodingUtils.readTimestamp(buffer); + } + + if ((_propertyFlags & TYPE_MASK) != 0) + { + _type = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & USER_ID_MASK) != 0) + { + _userId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & APPLICATION_ID_MASK) != 0) + { + _appId = EncodingUtils.readAMQShortString(buffer); + } + + if ((_propertyFlags & CLUSTER_ID_MASK) != 0) + { + _clusterId = EncodingUtils.readAMQShortString(buffer); + } + } + catch (AMQFrameDecodingException e) + { + throw new RuntimeException("Error in content header data: " + e, e); + } + + final int endPos = buffer.position(); + buffer.position(pos); + final int len = endPos - pos; + _encodedForm = new byte[len]; + final int limit = buffer.limit(); + buffer.limit(endPos); + buffer.get(_encodedForm, 0, len); + buffer.limit(limit); + buffer.position(endPos); + _decoded = true; + } + + private void decodeUpToHeaders() + { + ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + try + { + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + byte length = buffer.get(); + buffer.skip(length); + } + + if ((_propertyFlags & ENCONDING_MASK) != 0) + { + byte length = buffer.get(); + buffer.skip(length); + } + + if ((_propertyFlags & HEADERS_MASK) != 0) + { + _headers = EncodingUtils.readFieldTable(buffer); + + } + + _decodedHeaders = true; + } + catch (AMQFrameDecodingException e) + { + throw new RuntimeException("Error in content header data: " + e, e); + } + } + + private void decodeUpToContentType() + { + ByteBuffer buffer = ByteBuffer.wrap(_encodedForm); + + if ((_propertyFlags & (CONTENT_TYPE_MASK)) != 0) + { + _contentType = EncodingUtils.readAMQShortString(buffer); + } + + _decodedContentType = true; + } + + private void decodeIfNecessary() + { + if (!_decoded) + { + // decode(); + } + } + + private void decodeHeadersIfNecessary() + { + if (!_decoded && !_decodedHeaders) + { + decodeUpToHeaders(); + } + } + + private void decodeContentTypeIfNecessary() + { + if (!_decoded && !_decodedContentType) + { + decodeUpToContentType(); + } + } + + public AMQShortString getContentType() + { + decodeContentTypeIfNecessary(); + + return _contentType; + } + + public String getContentTypeAsString() + { + decodeContentTypeIfNecessary(); + + return (_contentType == null) ? null : _contentType.toString(); + } + + public void setContentType(AMQShortString contentType) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= (CONTENT_TYPE_MASK); + _contentType = contentType; + } + + public void setContentType(String contentType) + { + _hasBeenUpdated = true; + setContentType((contentType == null) ? null : new AMQShortString(contentType)); + } + + public String getEncodingAsString() + { + + return (getEncoding() == null) ? null : getEncoding().toString(); + } + + public AMQShortString getEncoding() + { + decodeIfNecessary(); + + return _encoding; + } + + public void setEncoding(String encoding) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= ENCONDING_MASK; + _encoding = (encoding == null) ? null : new AMQShortString(encoding); + } + + public void setEncoding(AMQShortString encoding) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= ENCONDING_MASK; + _encoding = encoding; + } + + public FieldTable getHeaders() + { + decodeHeadersIfNecessary(); + + if (_headers == null) + { + setHeaders(FieldTableFactory.newFieldTable()); + } + + return _headers; + } + + public void setHeaders(FieldTable headers) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= HEADERS_MASK; + _headers = headers; + } + + public byte getDeliveryMode() + { + decodeIfNecessary(); + + return _deliveryMode; + } + + public void setDeliveryMode(byte deliveryMode) + { + clearEncodedForm(); + _propertyFlags |= DELIVERY_MODE_MASK; + _deliveryMode = deliveryMode; + } + + public byte getPriority() + { + decodeIfNecessary(); + + return _priority; + } + + public void setPriority(byte priority) + { + clearEncodedForm(); + _propertyFlags |= PROPRITY_MASK; + _priority = priority; + } + + public AMQShortString getCorrelationId() + { + decodeIfNecessary(); + + return _correlationId; + } + + public String getCorrelationIdAsString() + { + decodeIfNecessary(); + + return (_correlationId == null) ? null : _correlationId.toString(); + } + + public void setCorrelationId(String correlationId) + { + _hasBeenUpdated = true; + setCorrelationId((correlationId == null) ? null : new AMQShortString(correlationId)); + } + + public void setCorrelationId(AMQShortString correlationId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= CORRELATION_ID_MASK; + _correlationId = correlationId; + } + + public String getReplyToAsString() + { + decodeIfNecessary(); + + return (_replyTo == null) ? null : _replyTo.toString(); + } + + public AMQShortString getReplyTo() + { + decodeIfNecessary(); + + return _replyTo; + } + + public void setReplyTo(String replyTo) + { + _hasBeenUpdated = true; + setReplyTo((replyTo == null) ? null : new AMQShortString(replyTo)); + } + + public void setReplyTo(AMQShortString replyTo) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= REPLY_TO_MASK; + _replyTo = replyTo; + } + + public long getExpiration() + { + decodeIfNecessary(); + return _expiration; + } + + public void setExpiration(long expiration) + { + clearEncodedForm(); + _propertyFlags |= EXPIRATION_MASK; + _expiration = expiration; + } + + public AMQShortString getMessageId() + { + decodeIfNecessary(); + + return _messageId; + } + + public String getMessageIdAsString() + { + decodeIfNecessary(); + + return (_messageId == null) ? null : _messageId.toString(); + } + + public void setMessageId(String messageId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= MESSAGE_ID_MASK; + _messageId = (messageId == null) ? null : new AMQShortString(messageId); + } + + public void setMessageId(AMQShortString messageId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= MESSAGE_ID_MASK; + _messageId = messageId; + } + + public long getTimestamp() + { + decodeIfNecessary(); + return _timestamp; + } + + public void setTimestamp(long timestamp) + { + clearEncodedForm(); + _propertyFlags |= TIMESTAMP_MASK; + _timestamp = timestamp; + } + + public String getTypeAsString() + { + decodeIfNecessary(); + + return (_type == null) ? null : _type.toString(); + } + + public AMQShortString getType() + { + decodeIfNecessary(); + + return _type; + } + + public void setType(String type) + { + _hasBeenUpdated = true; + setType((type == null) ? null : new AMQShortString(type)); + } + + public void setType(AMQShortString type) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= TYPE_MASK; + _type = type; + } + + public String getUserIdAsString() + { + decodeIfNecessary(); + + return (_userId == null) ? null : _userId.toString(); + } + + public AMQShortString getUserId() + { + decodeIfNecessary(); + + return _userId; + } + + public void setUserId(String userId) + { + setUserId((userId == null) ? null : new AMQShortString(userId)); + } + + public void setUserId(AMQShortString userId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= USER_ID_MASK; + _userId = userId; + } + + public String getAppIdAsString() + { + decodeIfNecessary(); + + return (_appId == null) ? null : _appId.toString(); + } + + public AMQShortString getAppId() + { + decodeIfNecessary(); + + return _appId; + } + + public void setAppId(String appId) + { + _hasBeenUpdated = true; + setAppId((appId == null) ? null : new AMQShortString(appId)); + } + + public void setAppId(AMQShortString appId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= APPLICATION_ID_MASK; + _appId = appId; + _hasBeenUpdated = true; + } + + public String getClusterIdAsString() + { + _hasBeenUpdated = true; + decodeIfNecessary(); + return (_clusterId == null) ? null : _clusterId.toString(); + } + + public AMQShortString getClusterId() + { + _hasBeenUpdated = true; + decodeIfNecessary(); + return _clusterId; + } + + public void setClusterId(String clusterId) + { + _hasBeenUpdated = true; + setClusterId((clusterId == null) ? null : new AMQShortString(clusterId)); + } + + public void setClusterId(AMQShortString clusterId) + { + _hasBeenUpdated = true; + clearEncodedForm(); + _propertyFlags |= CLUSTER_ID_MASK; + _clusterId = clusterId; + } + + public String toString() + { + return "reply-to = " + _replyTo + ",propertyFlags = " + _propertyFlags + ",ApplicationID = " + _appId + + ",ClusterID = " + _clusterId + ",UserId = " + _userId + ",JMSMessageID = " + _messageId + + ",JMSCorrelationID = " + _correlationId + ",JMSDeliveryMode = " + _deliveryMode + ",JMSExpiration = " + + _expiration + ",JMSPriority = " + _priority + ",JMSTimestamp = " + _timestamp + ",JMSType = " + _type; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.java new file mode 100644 index 0000000000..59646577e1 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/BodyFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * Any class that is capable of turning a stream of bytes into an AMQ structure must implement this interface. + */ +public interface BodyFactory +{ + AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException; +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.java new file mode 100644 index 0000000000..7162c37062 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/CommonContentHeaderProperties.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.framing; + +public interface CommonContentHeaderProperties extends ContentHeaderProperties +{ + AMQShortString getContentType(); + + void setContentType(AMQShortString contentType); + + FieldTable getHeaders(); + + void setHeaders(FieldTable headers); + + byte getDeliveryMode(); + + void setDeliveryMode(byte deliveryMode); + + byte getPriority(); + + void setPriority(byte priority); + + AMQShortString getCorrelationId(); + + void setCorrelationId(AMQShortString correlationId); + + AMQShortString getReplyTo(); + + void setReplyTo(AMQShortString replyTo); + + long getExpiration(); + + void setExpiration(long expiration); + + AMQShortString getMessageId(); + + void setMessageId(AMQShortString messageId); + + long getTimestamp(); + + void setTimestamp(long timestamp); + + AMQShortString getType(); + + void setType(AMQShortString type); + + AMQShortString getUserId(); + + void setUserId(AMQShortString userId); + + AMQShortString getAppId(); + + void setAppId(AMQShortString appId); + + AMQShortString getClusterId(); + + void setClusterId(AMQShortString clusterId); + + AMQShortString getEncoding(); + + void setEncoding(AMQShortString encoding); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.java new file mode 100644 index 0000000000..94030f383e --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/CompositeAMQDataBlock.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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class CompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock +{ + + private AMQDataBlock[] _blocks; + + public CompositeAMQDataBlock(AMQDataBlock[] blocks) + { + _blocks = blocks; + } + + + public AMQDataBlock[] getBlocks() + { + return _blocks; + } + + + public long getSize() + { + long frameSize = 0; + for (int i = 0; i < _blocks.length; i++) + { + frameSize += _blocks[i].getSize(); + } + return frameSize; + } + + public void writePayload(ByteBuffer buffer) + { + for (int i = 0; i < _blocks.length; i++) + { + _blocks[i].writePayload(buffer); + } + } + + public String toString() + { + if (_blocks == null) + { + return "No blocks contained in composite frame"; + } + else + { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{"); + for (int i = 0 ; i < _blocks.length; i++) + { + buf.append(" ").append(i).append("=[").append(_blocks[i].toString()).append("]"); + } + buf.append("}"); + return buf.toString(); + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/Content.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/Content.java new file mode 100644 index 0000000000..e5feeec2a4 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/Content.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.framing; + +public interface Content +{ + // TODO: New Content class required for AMQP 0-9. +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBody.java new file mode 100644 index 0000000000..9d39f8aa86 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBody.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.AMQException; + +public class ContentBody implements AMQBody +{ + public static final byte TYPE = 3; + + public ByteBuffer payload; + + public ContentBody() + { + } + + public ContentBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if (size > 0) + { + payload = buffer.slice(); + payload.limit((int) size); + buffer.skip((int) size); + } + + } + + + public ContentBody(ByteBuffer payload) + { + this.payload = payload; + } + + public byte getFrameType() + { + return TYPE; + } + + public int getSize() + { + return (payload == null ? 0 : payload.limit()); + } + + public void writePayload(ByteBuffer buffer) + { + if (payload != null) + { + if(payload.isDirect() || payload.isReadOnly()) + { + ByteBuffer copy = payload.duplicate(); + buffer.put(copy.rewind()); + } + else + { + buffer.put(payload.array(),payload.arrayOffset(),payload.limit()); + } + } + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) + throws AMQException + { + session.contentBodyReceived(channelId, this); + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if (size > 0) + { + payload = buffer.slice(); + payload.limit((int) size); + buffer.skip((int) size); + } + + } + + public void reduceBufferToFit() + { + if (payload != null && (payload.remaining() < payload.capacity() / 2)) + { + int size = payload.limit(); + ByteBuffer newPayload = ByteBuffer.allocate(size); + + newPayload.put(payload); + newPayload.flip(); + + //reduce reference count on payload + payload.release(); + + payload = newPayload; + } + } + + + + public static AMQFrame createAMQFrame(int channelId, ContentBody body) + { + final AMQFrame frame = new AMQFrame(channelId, body); + return frame; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.java new file mode 100644 index 0000000000..c42995d148 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentBodyFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ContentBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private static final ContentBodyFactory _instance = new ContentBodyFactory(); + + public static ContentBodyFactory getInstance() + { + return _instance; + } + + private ContentBodyFactory() + { + _log.debug("Creating content body factory"); + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return new ContentBody(in, bodySize); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.java new file mode 100644 index 0000000000..83e5a7e341 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBody.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.AMQException; + +public class ContentHeaderBody implements AMQBody +{ + public static final byte TYPE = 2; + + public int classId; + + public int weight; + + /** unsigned long but java can't handle that anyway when allocating byte array */ + public long bodySize; + + /** must never be null */ + public ContentHeaderProperties properties; + + public ContentHeaderBody() + { + } + + public ContentHeaderBody(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + classId = buffer.getUnsignedShort(); + weight = buffer.getUnsignedShort(); + bodySize = buffer.getLong(); + int propertyFlags = buffer.getUnsignedShort(); + ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance(); + properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14); + + } + + + public ContentHeaderBody(ContentHeaderProperties props, int classId) + { + properties = props; + this.classId = classId; + } + + public ContentHeaderBody(int classId, int weight, ContentHeaderProperties props, long bodySize) + { + this(props, classId); + this.weight = weight; + this.bodySize = bodySize; + } + + public byte getFrameType() + { + return TYPE; + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + classId = buffer.getUnsignedShort(); + weight = buffer.getUnsignedShort(); + bodySize = buffer.getLong(); + int propertyFlags = buffer.getUnsignedShort(); + ContentHeaderPropertiesFactory factory = ContentHeaderPropertiesFactory.getInstance(); + properties = factory.createContentHeaderProperties(classId, propertyFlags, buffer, (int)size - 14); + } + + /** + * Helper method that is used currently by the persistence layer (by BDB at the moment). + * @param buffer + * @param size + * @return + * @throws AMQFrameDecodingException + */ + public static ContentHeaderBody createFromBuffer(ByteBuffer buffer, long size) + throws AMQFrameDecodingException, AMQProtocolVersionException + { + ContentHeaderBody body = new ContentHeaderBody(buffer, size); + + return body; + } + + public int getSize() + { + return 2 + 2 + 8 + 2 + properties.getPropertyListSize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, classId); + EncodingUtils.writeUnsignedShort(buffer, weight); + buffer.putLong(bodySize); + EncodingUtils.writeUnsignedShort(buffer, properties.getPropertyFlags()); + properties.writePropertyListPayload(buffer); + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) + throws AMQException + { + session.contentHeaderReceived(channelId, this); + } + + public static AMQFrame createAMQFrame(int channelId, int classId, int weight, BasicContentHeaderProperties properties, + long bodySize) + { + return new AMQFrame(channelId, new ContentHeaderBody(classId, weight, properties, bodySize)); + } + + public static AMQFrame createAMQFrame(int channelId, ContentHeaderBody body) + { + return new AMQFrame(channelId, body); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.java new file mode 100644 index 0000000000..8d5e2f9fb4 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderBodyFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ContentHeaderBodyFactory implements BodyFactory +{ + private static final Logger _log = LoggerFactory.getLogger(AMQMethodBodyFactory.class); + + private static final ContentHeaderBodyFactory _instance = new ContentHeaderBodyFactory(); + + public static ContentHeaderBodyFactory getInstance() + { + return _instance; + } + + private ContentHeaderBodyFactory() + { + _log.debug("Creating content header body factory"); + } + + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + // all content headers are the same - it is only the properties that differ. + // the content header body further delegates construction of properties + return new ContentHeaderBody(in, bodySize); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.java new file mode 100644 index 0000000000..7ef538cfdc --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderProperties.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.framing; + +import org.apache.mina.common.ByteBuffer; + +/** + * There will be an implementation of this interface for each content type. All content types have associated + * header properties and this provides a way to encode and decode them. + */ +public interface ContentHeaderProperties +{ + /** + * Writes the property list to the buffer, in a suitably encoded form. + * @param buffer The buffer to write to + */ + void writePropertyListPayload(ByteBuffer buffer); + + /** + * Populates the properties from buffer. + * @param buffer The buffer to read from. + * @param propertyFlags he property flags. + * @throws AMQFrameDecodingException when the buffer does not contain valid data + */ + void populatePropertiesFromBuffer(ByteBuffer buffer, int propertyFlags, int size) + throws AMQFrameDecodingException; + + /** + * @return the size of the encoded property list in bytes. + */ + int getPropertyListSize(); + + /** + * Gets the property flags. Property flags indicate which properties are set in the list. The + * position and meaning of each flag is defined in the protocol specification for the particular + * content type with which these properties are associated. + * @return flags + */ + int getPropertyFlags(); + + void updated(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.java new file mode 100644 index 0000000000..46189b63d7 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ContentHeaderPropertiesFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.amqp_8_0.BasicConsumeBodyImpl; + +public class ContentHeaderPropertiesFactory +{ + private static final ContentHeaderPropertiesFactory _instance = new ContentHeaderPropertiesFactory(); + + public static ContentHeaderPropertiesFactory getInstance() + { + return _instance; + } + + private ContentHeaderPropertiesFactory() + { + } + + public ContentHeaderProperties createContentHeaderProperties(int classId, int propertyFlags, + ByteBuffer buffer, int size) + throws AMQFrameDecodingException + { + ContentHeaderProperties properties; + // AMQP version change: "Hardwired" version to major=8, minor=0 + // TODO: Change so that the actual version is obtained from + // the ProtocolInitiation object for this session. + if (classId == BasicConsumeBodyImpl.CLASS_ID) + { + properties = new BasicContentHeaderProperties(); + } + else + { + throw new AMQFrameDecodingException(null, "Unsupport content header class id: " + classId, null); + } + properties.populatePropertiesFromBuffer(buffer, propertyFlags, size); + return properties; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java new file mode 100644 index 0000000000..f6795ff200 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/DeferredDataBlock.java @@ -0,0 +1,50 @@ +package org.apache.qpid.framing; + +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 abstract class DeferredDataBlock extends AMQDataBlock +{ + private AMQDataBlock _underlyingDataBlock; + + + public long getSize() + { + if(_underlyingDataBlock == null) + { + _underlyingDataBlock = createAMQDataBlock(); + } + return _underlyingDataBlock.getSize(); + } + + public void writePayload(ByteBuffer buffer) + { + if(_underlyingDataBlock == null) + { + _underlyingDataBlock = createAMQDataBlock(); + } + _underlyingDataBlock.writePayload(buffer); + } + + abstract protected AMQDataBlock createAMQDataBlock(); + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.java new file mode 100644 index 0000000000..9cf96e698c --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodableAMQDataBlock.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.framing; + +/** + * Marker interface to indicate to MINA that a data block should be encoded with the + * single encoder/decoder that we have defined. + * + * Note that due to a bug in MINA all classes must directly implement this interface, even if + * a superclass implements it. + * TODO: fix MINA so that this is not necessary + * + */ +public interface EncodableAMQDataBlock +{ + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java new file mode 100644 index 0000000000..6425f8c591 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/EncodingUtils.java @@ -0,0 +1,1033 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; + +public class EncodingUtils +{ + private static final Logger _logger = LoggerFactory.getLogger(EncodingUtils.class); + + private static final String STRING_ENCODING = "iso8859-15"; + + private static final Charset _charset = Charset.forName("iso8859-15"); + + public static final int SIZEOF_UNSIGNED_SHORT = 2; + public static final int SIZEOF_UNSIGNED_INT = 4; + private static final boolean[] ALL_FALSE_ARRAY = new boolean[8]; + + public static int encodedShortStringLength(String s) + { + if (s == null) + { + return 1; + } + else + { + return (short) (1 + s.length()); + } + } + + public static int encodedShortStringLength(short s) + { + if (s == 0) + { + return 1 + 1; + } + + int len = 0; + if (s < 0) + { + len = 1; + // sloppy - doesn't work of Integer.MIN_VALUE + s = (short) -s; + } + + if (s > 9999) + { + return 1 + 5; + } + else if (s > 999) + { + return 1 + 4; + } + else if (s > 99) + { + return 1 + 3; + } + else if (s > 9) + { + return 1 + 2; + } + else + { + return 1 + 1; + } + + } + + public static int encodedShortStringLength(int i) + { + if (i == 0) + { + return 1 + 1; + } + + int len = 0; + if (i < 0) + { + len = 1; + // sloppy - doesn't work of Integer.MIN_VALUE + i = -i; + } + + // range is now 1 - 2147483647 + if (i < Short.MAX_VALUE) + { + return len + encodedShortStringLength((short) i); + } + else if (i > 999999) + { + return len + 6 + encodedShortStringLength((short) (i / 1000000)); + } + else // if (i > 99999) + { + return len + 5 + encodedShortStringLength((short) (i / 100000)); + } + + } + + public static int encodedShortStringLength(long l) + { + if (l == 0) + { + return 1 + 1; + } + + int len = 0; + if (l < 0) + { + len = 1; + // sloppy - doesn't work of Long.MIN_VALUE + l = -l; + } + + if (l < Integer.MAX_VALUE) + { + return len + encodedShortStringLength((int) l); + } + else if (l > 9999999999L) + { + return len + 10 + encodedShortStringLength((int) (l / 10000000000L)); + } + else + { + return len + 1 + encodedShortStringLength((int) (l / 10L)); + } + + } + + public static int encodedShortStringLength(AMQShortString s) + { + if (s == null) + { + return 1; + } + else + { + return (1 + s.length()); + } + } + + public static int encodedLongStringLength(String s) + { + if (s == null) + { + return 4; + } + else + { + return 4 + s.length(); + } + } + + public static int encodedLongStringLength(char[] s) + { + if (s == null) + { + return 4; + } + else + { + return 4 + s.length; + } + } + + public static int encodedLongstrLength(byte[] bytes) + { + if (bytes == null) + { + return 4; + } + else + { + return 4 + bytes.length; + } + } + + public static int encodedFieldTableLength(FieldTable table) + { + if (table == null) + { + // length is encoded as 4 octets + return 4; + } + else + { + // length of the table plus 4 octets for the length + return (int) table.getEncodedSize() + 4; + } + } + + public static int encodedContentLength(Content table) + { + // TODO: New Content class required for AMQP 0-9. + return 0; + } + + public static void writeShortStringBytes(ByteBuffer buffer, String s) + { + if (s != null) + { + byte[] encodedString = new byte[s.length()]; + char[] cha = s.toCharArray(); + for (int i = 0; i < cha.length; i++) + { + encodedString[i] = (byte) cha[i]; + } + + // TODO: check length fits in an unsigned byte + writeUnsignedByte(buffer, (short)encodedString.length); + buffer.put(encodedString); + + + } + else + { + // really writing out unsigned byte + buffer.put((byte) 0); + } + } + + public static void writeShortStringBytes(ByteBuffer buffer, AMQShortString s) + { + if (s != null) + { + + s.writeToBuffer(buffer); + } + else + { + // really writing out unsigned byte + buffer.put((byte) 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, String s) + { + assert (s == null) || (s.length() <= 0xFFFE); + if (s != null) + { + int len = s.length(); + writeUnsignedInteger(buffer, s.length()); + byte[] encodedString = new byte[len]; + char[] cha = s.toCharArray(); + for (int i = 0; i < cha.length; i++) + { + encodedString[i] = (byte) cha[i]; + } + + buffer.put(encodedString); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, char[] s) + { + assert (s == null) || (s.length <= 0xFFFE); + if (s != null) + { + int len = s.length; + writeUnsignedInteger(buffer, s.length); + byte[] encodedString = new byte[len]; + for (int i = 0; i < s.length; i++) + { + encodedString[i] = (byte) s[i]; + } + + buffer.put(encodedString); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeLongStringBytes(ByteBuffer buffer, byte[] bytes) + { + assert (bytes == null) || (bytes.length <= 0xFFFE); + if (bytes != null) + { + writeUnsignedInteger(buffer, bytes.length); + buffer.put(bytes); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeUnsignedByte(ByteBuffer buffer, short b) + { + byte bv = (byte) b; + buffer.put(bv); + } + + public static void writeUnsignedShort(ByteBuffer buffer, int s) + { + // TODO: Is this comparison safe? Do I need to cast RHS to long? + if (s < Short.MAX_VALUE) + { + buffer.putShort((short) s); + } + else + { + short sv = (short) s; + buffer.put((byte) (0xFF & (sv >> 8))); + buffer.put((byte) (0xFF & sv)); + } + } + + public static int unsignedIntegerLength() + { + return 4; + } + + public static void writeUnsignedInteger(ByteBuffer buffer, long l) + { + // TODO: Is this comparison safe? Do I need to cast RHS to long? + if (l < Integer.MAX_VALUE) + { + buffer.putInt((int) l); + } + else + { + int iv = (int) l; + + // FIXME: This *may* go faster if we build this into a local 4-byte array and then + // put the array in a single call. + buffer.put((byte) (0xFF & (iv >> 24))); + buffer.put((byte) (0xFF & (iv >> 16))); + buffer.put((byte) (0xFF & (iv >> 8))); + buffer.put((byte) (0xFF & iv)); + } + } + + public static void writeFieldTableBytes(ByteBuffer buffer, FieldTable table) + { + if (table != null) + { + table.writeToBuffer(buffer); + } + else + { + EncodingUtils.writeUnsignedInteger(buffer, 0); + } + } + + public static void writeContentBytes(ByteBuffer buffer, Content content) + { + // TODO: New Content class required for AMQP 0-9. + } + + public static void writeBooleans(ByteBuffer buffer, boolean[] values) + { + byte packedValue = 0; + for (int i = 0; i < values.length; i++) + { + if (values[i]) + { + packedValue = (byte) (packedValue | (1 << i)); + } + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value) + { + + buffer.put(value ? (byte) 1 : (byte) 0); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5, boolean value6) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + if (value6) + { + packedValue = (byte) (packedValue | (byte) (1 << 6)); + } + + buffer.put(packedValue); + } + + public static void writeBooleans(ByteBuffer buffer, boolean value0, boolean value1, boolean value2, boolean value3, + boolean value4, boolean value5, boolean value6, boolean value7) + { + byte packedValue = value0 ? (byte) 1 : (byte) 0; + + if (value1) + { + packedValue = (byte) (packedValue | (byte) (1 << 1)); + } + + if (value2) + { + packedValue = (byte) (packedValue | (byte) (1 << 2)); + } + + if (value3) + { + packedValue = (byte) (packedValue | (byte) (1 << 3)); + } + + if (value4) + { + packedValue = (byte) (packedValue | (byte) (1 << 4)); + } + + if (value5) + { + packedValue = (byte) (packedValue | (byte) (1 << 5)); + } + + if (value6) + { + packedValue = (byte) (packedValue | (byte) (1 << 6)); + } + + if (value7) + { + packedValue = (byte) (packedValue | (byte) (1 << 7)); + } + + buffer.put(packedValue); + } + + /** + * This is used for writing longstrs. + * + * @param buffer + * @param data + */ + public static void writeLongstr(ByteBuffer buffer, byte[] data) + { + if (data != null) + { + writeUnsignedInteger(buffer, data.length); + buffer.put(data); + } + else + { + writeUnsignedInteger(buffer, 0); + } + } + + public static void writeTimestamp(ByteBuffer buffer, long timestamp) + { + writeLong(buffer, timestamp); + } + + public static boolean[] readBooleans(ByteBuffer buffer) + { + final byte packedValue = buffer.get(); + if (packedValue == 0) + { + return ALL_FALSE_ARRAY; + } + + final boolean[] result = new boolean[8]; + + result[0] = ((packedValue & 1) != 0); + result[1] = ((packedValue & (1 << 1)) != 0); + result[2] = ((packedValue & (1 << 2)) != 0); + result[3] = ((packedValue & (1 << 3)) != 0); + if ((packedValue & 0xF0) == 0) + { + result[0] = ((packedValue & 1) != 0); + } + + result[4] = ((packedValue & (1 << 4)) != 0); + result[5] = ((packedValue & (1 << 5)) != 0); + result[6] = ((packedValue & (1 << 6)) != 0); + result[7] = ((packedValue & (1 << 7)) != 0); + + return result; + } + + public static FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return null; + } + else + { + return FieldTableFactory.newFieldTable(buffer, length); + } + } + + public static Content readContent(ByteBuffer buffer) throws AMQFrameDecodingException + { + // TODO: New Content class required for AMQP 0-9. + return null; + } + + public static AMQShortString readAMQShortString(ByteBuffer buffer) + { + return AMQShortString.readFromBuffer(buffer); + + } + + public static String readShortString(ByteBuffer buffer) + { + short length = buffer.getUnsigned(); + if (length == 0) + { + return null; + } + else + { + // this may seem rather odd to declare two array but testing has shown + // that constructing a string from a byte array is 5 (five) times slower + // than constructing one from a char array. + // this approach here is valid since we know that all the chars are + // ASCII (0-127) + byte[] stringBytes = new byte[length]; + buffer.get(stringBytes, 0, length); + char[] stringChars = new char[length]; + for (int i = 0; i < stringChars.length; i++) + { + stringChars[i] = (char) stringBytes[i]; + } + + return new String(stringChars); + } + } + + public static String readLongString(ByteBuffer buffer) + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return ""; + } + else + { + // this may seem rather odd to declare two array but testing has shown + // that constructing a string from a byte array is 5 (five) times slower + // than constructing one from a char array. + // this approach here is valid since we know that all the chars are + // ASCII (0-127) + byte[] stringBytes = new byte[(int) length]; + buffer.get(stringBytes, 0, (int) length); + char[] stringChars = new char[(int) length]; + for (int i = 0; i < stringChars.length; i++) + { + stringChars[i] = (char) stringBytes[i]; + } + + return new String(stringChars); + } + } + + public static byte[] readLongstr(ByteBuffer buffer) + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return null; + } + else + { + byte[] result = new byte[(int) length]; + buffer.get(result); + + return result; + } + } + + public static long readTimestamp(ByteBuffer buffer) + { + // Discard msb from AMQ timestamp + // buffer.getUnsignedInt(); + return buffer.getLong(); + } + + static byte[] hexToByteArray(String id) + { + // Should check param for null, long enough for this check, upper-case and trailing char + String s = (id.charAt(1) == 'x') ? id.substring(2) : id; // strip 0x + + int len = s.length(); + int byte_len = len / 2; + byte[] b = new byte[byte_len]; + + for (int i = 0; i < byte_len; i++) + { + // fixme: refine these repetitive subscript calcs. + int ch = i * 2; + + byte b1 = Byte.parseByte(s.substring(ch, ch + 1), 16); + byte b2 = Byte.parseByte(s.substring(ch + 1, ch + 2), 16); + + b[i] = (byte) ((b1 * 16) + b2); + } + + return (b); + } + + public static char[] convertToHexCharArray(byte[] from) + { + int length = from.length; + char[] result_buff = new char[(length * 2) + 2]; + + result_buff[0] = '0'; + result_buff[1] = 'x'; + + int bite; + int dest = 2; + + for (int i = 0; i < length; i++) + { + bite = from[i]; + + if (bite < 0) + { + bite += 256; + } + + result_buff[dest++] = hex_chars[bite >> 4]; + result_buff[dest++] = hex_chars[bite & 0x0f]; + } + + return (result_buff); + } + + public static String convertToHexString(byte[] from) + { + return (new String(convertToHexCharArray(from))); + } + + public static String convertToHexString(ByteBuffer bb) + { + int size = bb.limit(); + + byte[] from = new byte[size]; + + // Is this not the same. + // bb.get(from, 0, length); + for (int i = 0; i < size; i++) + { + from[i] = bb.get(i); + } + + return (new String(convertToHexCharArray(from))); + } + + private static char[] hex_chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + // **** new methods + + // AMQP_BOOLEAN_PROPERTY_PREFIX + + public static void writeBoolean(ByteBuffer buffer, Boolean aBoolean) + { + buffer.put((byte) (aBoolean ? 1 : 0)); + } + + public static boolean readBoolean(ByteBuffer buffer) + { + byte packedValue = buffer.get(); + + return (packedValue == 1); + } + + public static int encodedBooleanLength() + { + return 1; + } + + // AMQP_BYTE_PROPERTY_PREFIX + public static void writeByte(ByteBuffer buffer, Byte aByte) + { + buffer.put(aByte); + } + + public static byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + public static int encodedByteLength() + { + return 1; + } + + // AMQP_SHORT_PROPERTY_PREFIX + public static void writeShort(ByteBuffer buffer, Short aShort) + { + buffer.putShort(aShort); + } + + public static short readShort(ByteBuffer buffer) + { + return buffer.getShort(); + } + + public static int encodedShortLength() + { + return 2; + } + + // INTEGER_PROPERTY_PREFIX + public static void writeInteger(ByteBuffer buffer, Integer aInteger) + { + buffer.putInt(aInteger); + } + + public static int readInteger(ByteBuffer buffer) + { + return buffer.getInt(); + } + + public static int encodedIntegerLength() + { + return 4; + } + + // AMQP_LONG_PROPERTY_PREFIX + public static void writeLong(ByteBuffer buffer, Long aLong) + { + buffer.putLong(aLong); + } + + public static long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + public static int encodedLongLength() + { + return 8; + } + + // Float_PROPERTY_PREFIX + public static void writeFloat(ByteBuffer buffer, Float aFloat) + { + buffer.putFloat(aFloat); + } + + public static float readFloat(ByteBuffer buffer) + { + return buffer.getFloat(); + } + + public static int encodedFloatLength() + { + return 4; + } + + // Double_PROPERTY_PREFIX + public static void writeDouble(ByteBuffer buffer, Double aDouble) + { + buffer.putDouble(aDouble); + } + + public static double readDouble(ByteBuffer buffer) + { + return buffer.getDouble(); + } + + public static int encodedDoubleLength() + { + return 8; + } + + public static byte[] readBytes(ByteBuffer buffer) + { + long length = buffer.getUnsignedInt(); + if (length == 0) + { + return null; + } + else + { + byte[] dataBytes = new byte[(int)length]; + buffer.get(dataBytes, 0, (int)length); + + return dataBytes; + } + } + + public static void writeBytes(ByteBuffer buffer, byte[] data) + { + if (data != null) + { + // TODO: check length fits in an unsigned byte + writeUnsignedInteger(buffer, (long)data.length); + buffer.put(data); + } + else + { + // really writing out unsigned byte + //buffer.put((byte) 0); + writeUnsignedInteger(buffer, 0L); + } + } + + // CHAR_PROPERTY + public static int encodedCharLength() + { + return encodedByteLength(); + } + + public static char readChar(ByteBuffer buffer) + { + // This is valid as we know that the Character is ASCII 0..127 + return (char) buffer.get(); + } + + public static void writeChar(ByteBuffer buffer, char character) + { + // This is valid as we know that the Character is ASCII 0..127 + writeByte(buffer, (byte) character); + } + + public static long readLongAsShortString(ByteBuffer buffer) + { + short length = buffer.getUnsigned(); + short pos = 0; + if (length == 0) + { + return 0L; + } + + byte digit = buffer.get(); + boolean isNegative; + long result = 0; + if (digit == (byte) '-') + { + isNegative = true; + pos++; + digit = buffer.get(); + } + else + { + isNegative = false; + } + + result = digit - (byte) '0'; + pos++; + + while (pos < length) + { + pos++; + digit = buffer.get(); + result = (result << 3) + (result << 1); + result += digit - (byte) '0'; + } + + return result; + } + + public static long readUnsignedInteger(ByteBuffer buffer) + { + long l = 0xFF & buffer.get(); + l <<= 8; + l = l | (0xFF & buffer.get()); + l <<= 8; + l = l | (0xFF & buffer.get()); + l <<= 8; + l = l | (0xFF & buffer.get()); + + return l; + } + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java new file mode 100644 index 0000000000..ed01c91804 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java @@ -0,0 +1,1187 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQPInvalidClassException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +// extends FieldTable +public class FieldTable +{ + private static final Logger _logger = LoggerFactory.getLogger(FieldTable.class); + private static final String STRICT_AMQP = "STRICT_AMQP"; + private final boolean _strictAMQP = Boolean.valueOf(System.getProperty(STRICT_AMQP, "false")); + + private ByteBuffer _encodedForm; + private LinkedHashMap _properties; + private long _encodedSize; + private static final int INITIAL_HASHMAP_CAPACITY = 16; + private static final int INITIAL_ENCODED_FORM_SIZE = 256; + + public FieldTable() + { + super(); + // _encodedForm = ByteBuffer.allocate(INITIAL_ENCODED_FORM_SIZE); + // _encodedForm.setAutoExpand(true); + // _encodedForm.limit(0); + } + + /** + * Construct a new field table. + * + * @param buffer the buffer from which to read data. The length byte must be read already + * @param length the length of the field table. Must be > 0. + * + * @throws AMQFrameDecodingException if there is an error decoding the table + */ + public FieldTable(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + this(); + _encodedForm = buffer.slice(); + _encodedForm.limit((int) length); + _encodedSize = length; + buffer.skip((int) length); + } + + public AMQTypedValue getProperty(AMQShortString string) + { + checkPropertyName(string); + + synchronized (this) + { + if (_properties == null) + { + if (_encodedForm == null) + { + return null; + } + else + { + populateFromBuffer(); + } + } + } + + if (_properties == null) + { + return null; + } + else + { + return _properties.get(string); + } + } + + private void populateFromBuffer() + { + try + { + setFromBuffer(_encodedForm, _encodedSize); + } + catch (AMQFrameDecodingException e) + { + _logger.error("Error decoding FieldTable in deferred decoding mode ", e); + throw new IllegalArgumentException(e); + } + } + + private AMQTypedValue setProperty(AMQShortString key, AMQTypedValue val) + { + checkPropertyName(key); + initMapIfNecessary(); + if (_properties.containsKey(key)) + { + _encodedForm = null; + + if (val == null) + { + return removeKey(key); + } + } + else if ((_encodedForm != null) && (val != null)) + { + EncodingUtils.writeShortStringBytes(_encodedForm, key); + val.writeToBuffer(_encodedForm); + + } + else if (val == null) + { + return null; + } + + AMQTypedValue oldVal = _properties.put(key, val); + if (oldVal != null) + { + _encodedSize -= oldVal.getEncodingSize(); + } + else + { + _encodedSize += EncodingUtils.encodedShortStringLength(key) + 1; + } + + _encodedSize += val.getEncodingSize(); + + return oldVal; + } + + private void initMapIfNecessary() + { + synchronized (this) + { + if (_properties == null) + { + if ((_encodedForm == null) || (_encodedSize == 0)) + { + _properties = new LinkedHashMap(); + } + else + { + populateFromBuffer(); + } + } + + } + } + + public Boolean getBoolean(String string) + { + return getBoolean(new AMQShortString(string)); + } + + public Boolean getBoolean(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BOOLEAN)) + { + return (Boolean) value.getValue(); + } + else + { + return null; + } + } + + public Byte getByte(String string) + { + return getByte(new AMQShortString(string)); + } + + public Byte getByte(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BYTE)) + { + return (Byte) value.getValue(); + } + else + { + return null; + } + } + + public Short getShort(String string) + { + return getShort(new AMQShortString(string)); + } + + public Short getShort(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.SHORT)) + { + return (Short) value.getValue(); + } + else + { + return null; + } + } + + public Integer getInteger(String string) + { + return getInteger(new AMQShortString(string)); + } + + public Integer getInteger(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.INT)) + { + return (Integer) value.getValue(); + } + else + { + return null; + } + } + + public Long getLong(String string) + { + return getLong(new AMQShortString(string)); + } + + public Long getLong(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.LONG)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public Float getFloat(String string) + { + return getFloat(new AMQShortString(string)); + } + + public Float getFloat(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.FLOAT)) + { + return (Float) value.getValue(); + } + else + { + return null; + } + } + + public Double getDouble(String string) + { + return getDouble(new AMQShortString(string)); + } + + public Double getDouble(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.DOUBLE)) + { + return (Double) value.getValue(); + } + else + { + return null; + } + } + + public String getString(String string) + { + return getString(new AMQShortString(string)); + } + + public String getString(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && ((value.getType() == AMQType.WIDE_STRING) || (value.getType() == AMQType.ASCII_STRING))) + { + return (String) value.getValue(); + } + else if ((value != null) && (value.getValue() != null) && !(value.getValue() instanceof byte[])) + { + return String.valueOf(value.getValue()); + } + else + { + return null; + } + + } + + public Character getCharacter(String string) + { + return getCharacter(new AMQShortString(string)); + } + + public Character getCharacter(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.ASCII_CHARACTER)) + { + return (Character) value.getValue(); + } + else + { + return null; + } + } + + public byte[] getBytes(String string) + { + return getBytes(new AMQShortString(string)); + } + + public byte[] getBytes(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if ((value != null) && (value.getType() == AMQType.BINARY)) + { + return (byte[]) value.getValue(); + } + else + { + return null; + } + } + + /** + * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name. + * + * @param string The name of the parameter to get the associated FieldTable value for. + * + * @return The associated FieldTable value, or null if the associated value is not of FieldTable type or + * not present in the field table at all. + */ + public FieldTable getFieldTable(String string) + { + return getFieldTable(new AMQShortString(string)); + } + + /** + * Extracts a value from the field table that is itself a FieldTable associated with the specified parameter name. + * + * @param string The name of the parameter to get the associated FieldTable value for. + * + * @return The associated FieldTable value, or null if the associated value is not of FieldTable type or + * not present in the field table at all. + */ + public FieldTable getFieldTable(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + + if ((value != null) && (value.getType() == AMQType.FIELD_TABLE)) + { + return (FieldTable) value.getValue(); + } + else + { + return null; + } + } + + public Object getObject(String string) + { + return getObject(new AMQShortString(string)); + } + + public Object getObject(AMQShortString string) + { + AMQTypedValue value = getProperty(string); + if (value != null) + { + return value.getValue(); + } + else + { + return value; + } + + } + + public Long getTimestamp(AMQShortString name) + { + AMQTypedValue value = getProperty(name); + if ((value != null) && (value.getType() == AMQType.TIMESTAMP)) + { + return (Long) value.getValue(); + } + else + { + return null; + } + } + + public BigDecimal getDecimal(AMQShortString propertyName) + { + AMQTypedValue value = getProperty(propertyName); + if ((value != null) && (value.getType() == AMQType.DECIMAL)) + { + return (BigDecimal) value.getValue(); + } + else + { + return null; + } + } + + // ************ Setters + public Object setBoolean(String string, Boolean b) + { + return setBoolean(new AMQShortString(string), b); + } + + public Object setBoolean(AMQShortString string, Boolean b) + { + return setProperty(string, AMQType.BOOLEAN.asTypedValue(b)); + } + + public Object setByte(String string, Byte b) + { + return setByte(new AMQShortString(string), b); + } + + public Object setByte(AMQShortString string, Byte b) + { + return setProperty(string, AMQType.BYTE.asTypedValue(b)); + } + + public Object setShort(String string, Short i) + { + return setShort(new AMQShortString(string), i); + } + + public Object setShort(AMQShortString string, Short i) + { + return setProperty(string, AMQType.SHORT.asTypedValue(i)); + } + + public Object setInteger(String string, Integer i) + { + return setInteger(new AMQShortString(string), i); + } + + public Object setInteger(AMQShortString string, Integer i) + { + return setProperty(string, AMQType.INT.asTypedValue(i)); + } + + public Object setLong(String string, Long l) + { + return setLong(new AMQShortString(string), l); + } + + public Object setLong(AMQShortString string, Long l) + { + return setProperty(string, AMQType.LONG.asTypedValue(l)); + } + + public Object setFloat(String string, Float f) + { + return setFloat(new AMQShortString(string), f); + } + + public Object setFloat(AMQShortString string, Float v) + { + return setProperty(string, AMQType.FLOAT.asTypedValue(v)); + } + + public Object setDouble(String string, Double d) + { + return setDouble(new AMQShortString(string), d); + } + + public Object setDouble(AMQShortString string, Double v) + { + return setProperty(string, AMQType.DOUBLE.asTypedValue(v)); + } + + public Object setString(String string, String s) + { + return setString(new AMQShortString(string), s); + } + + public Object setAsciiString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.ASCII_STRING.asTypedValue(value)); + } + } + + public Object setString(AMQShortString string, String value) + { + if (value == null) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + else + { + return setProperty(string, AMQType.LONG_STRING.asTypedValue(value)); + } + } + + public Object setChar(String string, char c) + { + return setChar(new AMQShortString(string), c); + } + + public Object setChar(AMQShortString string, char c) + { + return setProperty(string, AMQType.ASCII_CHARACTER.asTypedValue(c)); + } + + public Object setBytes(String string, byte[] b) + { + return setBytes(new AMQShortString(string), b); + } + + public Object setBytes(AMQShortString string, byte[] bytes) + { + return setProperty(string, AMQType.BINARY.asTypedValue(bytes)); + } + + public Object setBytes(String string, byte[] bytes, int start, int length) + { + return setBytes(new AMQShortString(string), bytes, start, length); + } + + public Object setBytes(AMQShortString string, byte[] bytes, int start, int length) + { + byte[] newBytes = new byte[length]; + System.arraycopy(bytes, start, newBytes, 0, length); + + return setBytes(string, bytes); + } + + public Object setObject(String string, Object o) + { + return setObject(new AMQShortString(string), o); + } + + public Object setTimestamp(AMQShortString string, long datetime) + { + return setProperty(string, AMQType.TIMESTAMP.asTypedValue(datetime)); + } + + public Object setDecimal(AMQShortString string, BigDecimal decimal) + { + if (decimal.longValue() > Integer.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimals larger than " + Integer.MAX_VALUE); + } + + if (decimal.scale() > Byte.MAX_VALUE) + { + throw new UnsupportedOperationException("AMQP doesnot support decimal scales larger than " + Byte.MAX_VALUE); + } + + return setProperty(string, AMQType.DECIMAL.asTypedValue(decimal)); + } + + public Object setVoid(AMQShortString string) + { + return setProperty(string, AMQType.VOID.asTypedValue(null)); + } + + /** + * Associates a nested field table with the specified parameter name. + * + * @param string The name of the parameter to store in the table. + * @param ftValue The field table value to associate with the parameter name. + * + * @return The stored value. + */ + public Object setFieldTable(String string, FieldTable ftValue) + { + return setFieldTable(new AMQShortString(string), ftValue); + } + + /** + * Associates a nested field table with the specified parameter name. + * + * @param string The name of the parameter to store in the table. + * @param ftValue The field table value to associate with the parameter name. + * + * @return The stored value. + */ + public Object setFieldTable(AMQShortString string, FieldTable ftValue) + { + return setProperty(string, AMQType.FIELD_TABLE.asTypedValue(ftValue)); + } + + public Object setObject(AMQShortString string, Object object) + { + if (object instanceof Boolean) + { + return setBoolean(string, (Boolean) object); + } + else if (object instanceof Byte) + { + return setByte(string, (Byte) object); + } + else if (object instanceof Short) + { + return setShort(string, (Short) object); + } + else if (object instanceof Integer) + { + return setInteger(string, (Integer) object); + } + else if (object instanceof Long) + { + return setLong(string, (Long) object); + } + else if (object instanceof Float) + { + return setFloat(string, (Float) object); + } + else if (object instanceof Double) + { + return setDouble(string, (Double) object); + } + else if (object instanceof String) + { + return setString(string, (String) object); + } + else if (object instanceof Character) + { + return setChar(string, (Character) object); + } + else if (object instanceof byte[]) + { + return setBytes(string, (byte[]) object); + } + + throw new AMQPInvalidClassException("Only Primatives objects allowed Object is:" + object.getClass()); + } + + public boolean isNullStringValue(String name) + { + AMQTypedValue value = getProperty(new AMQShortString(name)); + + return (value != null) && (value.getType() == AMQType.VOID); + } + + // ***** Methods + + public Enumeration getPropertyNames() + { + return Collections.enumeration(keys()); + } + + public boolean propertyExists(AMQShortString propertyName) + { + return itemExists(propertyName); + } + + public boolean propertyExists(String propertyName) + { + return itemExists(propertyName); + } + + public boolean itemExists(AMQShortString propertyName) + { + checkPropertyName(propertyName); + initMapIfNecessary(); + + return _properties.containsKey(propertyName); + } + + public boolean itemExists(String string) + { + return itemExists(new AMQShortString(string)); + } + + public String toString() + { + initMapIfNecessary(); + + return _properties.toString(); + } + + private void checkPropertyName(AMQShortString 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"); + } + + if (_strictAMQP) + { + checkIdentiferFormat(propertyName); + } + } + + protected static void checkIdentiferFormat(AMQShortString propertyName) + { + // AMQP Spec: 4.2.5.5 Field Tables + // Guidelines for implementers: + // * Field names MUST start with a letter, '$' or '#' and may continue with + // letters, '$' or '#', digits, or underlines, to a maximum length of 128 + // characters. + // * The server SHOULD validate field names and upon receiving an invalid + // field name, it SHOULD signal a connection exception with reply code + // 503 (syntax error). Conformance test: amq_wlp_table_01. + // * A peer MUST handle duplicate fields by using only the first instance. + + // AMQP length limit + if (propertyName.length() > 128) + { + throw new IllegalArgumentException("AMQP limits property names to 128 characters"); + } + + // AMQ start character + if (!(Character.isLetter(propertyName.charAt(0)) || (propertyName.charAt(0) == '$') + || (propertyName.charAt(0) == '#') || (propertyName.charAt(0) == '_'))) // Not official AMQP added for JMS. + { + throw new IllegalArgumentException("Identifier '" + propertyName + + "' does not start with a valid AMQP start character"); + } + } + + // ************************* Byte Buffer Processing + + public void writeToBuffer(ByteBuffer buffer) + { + final boolean trace = _logger.isDebugEnabled(); + + if (trace) + { + _logger.debug("FieldTable::writeToBuffer: Writing encoded length of " + getEncodedSize() + "..."); + if (_properties != null) + { + _logger.debug(_properties.toString()); + } + } + + EncodingUtils.writeUnsignedInteger(buffer, getEncodedSize()); + + putDataInBuffer(buffer); + } + + public byte[] getDataAsBytes() + { + final int encodedSize = (int) getEncodedSize(); + final ByteBuffer buffer = ByteBuffer.allocate(encodedSize); // FIXME XXX: Is cast a problem? + + putDataInBuffer(buffer); + + final byte[] result = new byte[encodedSize]; + buffer.flip(); + buffer.get(result); + buffer.release(); + + return result; + } + + public long getEncodedSize() + { + return _encodedSize; + } + + private void recalculateEncodedSize() + { + + int encodedSize = 0; + if (_properties != null) + { + for (Map.Entry e : _properties.entrySet()) + { + encodedSize += EncodingUtils.encodedShortStringLength(e.getKey()); + encodedSize++; // the byte for the encoding Type + encodedSize += e.getValue().getEncodingSize(); + + } + } + + _encodedSize = encodedSize; + } + + public void addAll(FieldTable fieldTable) + { + initMapIfNecessary(); + _encodedForm = null; + _properties.putAll(fieldTable._properties); + recalculateEncodedSize(); + } + + public static interface FieldTableElementProcessor + { + public boolean processElement(String propertyName, AMQTypedValue value); + + public Object getResult(); + } + + public Object processOverElements(FieldTableElementProcessor processor) + { + initMapIfNecessary(); + if (_properties != null) + { + for (Map.Entry e : _properties.entrySet()) + { + boolean result = processor.processElement(e.getKey().toString(), e.getValue()); + if (!result) + { + break; + } + } + } + + return processor.getResult(); + + } + + public int size() + { + initMapIfNecessary(); + + return _properties.size(); + + } + + public boolean isEmpty() + { + return size() == 0; + } + + public boolean containsKey(AMQShortString key) + { + initMapIfNecessary(); + + return _properties.containsKey(key); + } + + public boolean containsKey(String key) + { + return containsKey(new AMQShortString(key)); + } + + public Set keys() + { + initMapIfNecessary(); + Set keys = new LinkedHashSet(); + for (AMQShortString key : _properties.keySet()) + { + keys.add(key.toString()); + } + + return keys; + } + + public Iterator> iterator() + { + if(_encodedForm != null) + { + return new FieldTableIterator(_encodedForm.duplicate().rewind(),(int)_encodedSize); + } + else + { + initMapIfNecessary(); + return _properties.entrySet().iterator(); + } + } + + + public Object get(AMQShortString key) + { + + return getObject(key); + } + + public Object put(AMQShortString key, Object value) + { + return setObject(key, value); + } + + public Object remove(String key) + { + + return remove(new AMQShortString(key)); + + } + + public Object remove(AMQShortString key) + { + AMQTypedValue val = removeKey(key); + + return (val == null) ? null : val.getValue(); + + } + + public AMQTypedValue removeKey(AMQShortString key) + { + initMapIfNecessary(); + _encodedForm = null; + AMQTypedValue value = _properties.remove(key); + if (value == null) + { + return null; + } + else + { + _encodedSize -= EncodingUtils.encodedShortStringLength(key); + _encodedSize--; + _encodedSize -= value.getEncodingSize(); + + return value; + } + + } + + public void clear() + { + initMapIfNecessary(); + _encodedForm = null; + _properties.clear(); + _encodedSize = 0; + } + + public Set keySet() + { + initMapIfNecessary(); + + return _properties.keySet(); + } + + private void putDataInBuffer(ByteBuffer buffer) + { + + if (_encodedForm != null) + { + if(buffer.isDirect() || buffer.isReadOnly()) + { + ByteBuffer encodedForm = _encodedForm.duplicate(); + + if (encodedForm.position() != 0) + { + encodedForm.flip(); + } + + buffer.put(encodedForm); + } + else + { + buffer.put(_encodedForm.array(),_encodedForm.arrayOffset(),(int)_encodedSize); + } + } + else if (_properties != null) + { + final Iterator> it = _properties.entrySet().iterator(); + + // If there are values then write out the encoded Size... could check _encodedSize != 0 + // write out the total length, which we have kept up to date as data is added + + while (it.hasNext()) + { + final Map.Entry me = it.next(); + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + // Write the actual parameter name + EncodingUtils.writeShortStringBytes(buffer, me.getKey()); + me.getValue().writeToBuffer(buffer); + } + catch (Exception e) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Exception thrown:" + e); + _logger.debug("Writing Property:" + me.getKey() + " Type:" + me.getValue().getType() + " Value:" + + me.getValue().getValue()); + _logger.debug("Buffer Position:" + buffer.position() + " Remaining:" + buffer.remaining()); + } + + throw new RuntimeException(e); + } + } + } + } + + private void setFromBuffer(ByteBuffer buffer, long length) throws AMQFrameDecodingException + { + + final boolean trace = _logger.isDebugEnabled(); + if (length > 0) + { + + final int expectedRemaining = buffer.remaining() - (int) length; + + _properties = new LinkedHashMap(INITIAL_HASHMAP_CAPACITY); + + do + { + + final AMQShortString key = EncodingUtils.readAMQShortString(buffer); + AMQTypedValue value = AMQTypedValue.readFromBuffer(buffer); + + if (trace) + { + _logger.debug("FieldTable::PropFieldTable(buffer," + length + "): Read type '" + value.getType() + + "', key '" + key + "', value '" + value.getValue() + "'"); + } + + _properties.put(key, value); + + } + while (buffer.remaining() > expectedRemaining); + + } + + _encodedSize = length; + + if (trace) + { + _logger.debug("FieldTable::FieldTable(buffer," + length + "): Done."); + } + } + + private static final class FieldTableEntry implements Map.Entry + { + private final AMQTypedValue _value; + private final AMQShortString _key; + + public FieldTableEntry(final AMQShortString key, final AMQTypedValue value) + { + _key = key; + _value = value; + } + + public AMQShortString getKey() + { + return _key; + } + + public AMQTypedValue getValue() + { + return _value; + } + + public AMQTypedValue setValue(final AMQTypedValue value) + { + throw new UnsupportedOperationException(); + } + + public boolean equals(Object o) + { + if(o instanceof FieldTableEntry) + { + FieldTableEntry other = (FieldTableEntry) o; + return (_key == null ? other._key == null : _key.equals(other._key)) + && (_value == null ? other._value == null : _value.equals(other._value)); + } + else + { + return false; + } + } + + public int hashCode() + { + return (getKey()==null ? 0 : getKey().hashCode()) + ^ (getValue()==null ? 0 : getValue().hashCode()); + } + + } + + + private static final class FieldTableIterator implements Iterator> + { + + private final ByteBuffer _buffer; + private int _expectedRemaining; + + public FieldTableIterator(ByteBuffer buffer, int length) + { + _buffer = buffer; + _expectedRemaining = buffer.remaining() - length; + } + + public boolean hasNext() + { + return (_buffer.remaining() > _expectedRemaining); + } + + public Map.Entry next() + { + if(hasNext()) + { + final AMQShortString key = EncodingUtils.readAMQShortString(_buffer); + AMQTypedValue value = AMQTypedValue.readFromBuffer(_buffer); + return new FieldTableEntry(key, value); + } + else + { + return null; + } + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + + + + public int hashCode() + { + initMapIfNecessary(); + + return _properties.hashCode(); + } + + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o == null) + { + return false; + } + + if (!(o instanceof FieldTable)) + { + return false; + } + + initMapIfNecessary(); + + FieldTable f = (FieldTable) o; + f.initMapIfNecessary(); + + return _properties.equals(f._properties); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.java new file mode 100644 index 0000000000..e9d75137ef --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/FieldTableFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class FieldTableFactory +{ + public static FieldTable newFieldTable() + { + return new FieldTable(); + } + + public static FieldTable newFieldTable(ByteBuffer byteBuffer, long length) throws AMQFrameDecodingException + { + return new FieldTable(byteBuffer, length); + } + + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.java new file mode 100644 index 0000000000..15a43345b5 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBody.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.apache.qpid.AMQException; + +public class HeartbeatBody implements AMQBody +{ + public static final byte TYPE = 8; + public static AMQFrame FRAME = new HeartbeatBody().toFrame(); + + public HeartbeatBody() + { + + } + + public HeartbeatBody(ByteBuffer buffer, long size) + { + if(size > 0) + { + //allow other implementations to have a payload, but ignore it: + buffer.skip((int) size); + } + } + + public byte getFrameType() + { + return TYPE; + } + + public int getSize() + { + return 0;//heartbeats we generate have no payload + } + + public void writePayload(ByteBuffer buffer) + { + } + + public void handle(final int channelId, final AMQVersionAwareProtocolSession session) + throws AMQException + { + session.heartbeatBodyReceived(channelId, this); + } + + protected void populateFromBuffer(ByteBuffer buffer, long size) throws AMQFrameDecodingException + { + if(size > 0) + { + //allow other implementations to have a payload, but ignore it: + buffer.skip((int) size); + } + } + + public AMQFrame toFrame() + { + return new AMQFrame(0, this); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.java new file mode 100644 index 0000000000..c7ada708dc --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/HeartbeatBodyFactory.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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class HeartbeatBodyFactory implements BodyFactory +{ + public AMQBody createBody(ByteBuffer in, long bodySize) throws AMQFrameDecodingException + { + return new HeartbeatBody(); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.java new file mode 100644 index 0000000000..3ac17e9204 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/ProtocolInitiation.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.framing; + +import org.apache.mina.common.ByteBuffer; +import org.apache.mina.common.IoSession; +import org.apache.mina.filter.codec.ProtocolDecoderOutput; +import org.apache.qpid.AMQException; + +import java.io.UnsupportedEncodingException; + +public class ProtocolInitiation extends AMQDataBlock implements EncodableAMQDataBlock +{ + + // TODO: generate these constants automatically from the xml protocol spec file + public static final byte[] AMQP_HEADER = new byte[]{(byte)'A',(byte)'M',(byte)'Q',(byte)'P'}; + + private static final byte CURRENT_PROTOCOL_CLASS = 1; + private static final byte TCP_PROTOCOL_INSTANCE = 1; + + public final byte[] _protocolHeader; + public final byte _protocolClass; + public final byte _protocolInstance; + public final byte _protocolMajor; + public final byte _protocolMinor; + + +// public ProtocolInitiation() {} + + public ProtocolInitiation(byte[] protocolHeader, byte protocolClass, byte protocolInstance, byte protocolMajor, byte protocolMinor) + { + _protocolHeader = protocolHeader; + _protocolClass = protocolClass; + _protocolInstance = protocolInstance; + _protocolMajor = protocolMajor; + _protocolMinor = protocolMinor; + } + + public ProtocolInitiation(ProtocolVersion pv) + { + this(AMQP_HEADER, CURRENT_PROTOCOL_CLASS, TCP_PROTOCOL_INSTANCE, pv.getMajorVersion(), pv.getMinorVersion()); + } + + + public ProtocolInitiation(ByteBuffer in) + { + _protocolHeader = new byte[4]; + in.get(_protocolHeader); + + _protocolClass = in.get(); + _protocolInstance = in.get(); + _protocolMajor = in.get(); + _protocolMinor = in.get(); + } + + public long getSize() + { + return 4 + 1 + 1 + 1 + 1; + } + + public void writePayload(ByteBuffer buffer) + { + + buffer.put(_protocolHeader); + buffer.put(_protocolClass); + buffer.put(_protocolInstance); + buffer.put(_protocolMajor); + buffer.put(_protocolMinor); + } + + public boolean equals(Object o) + { + if (!(o instanceof ProtocolInitiation)) + { + return false; + } + + ProtocolInitiation pi = (ProtocolInitiation) o; + if (pi._protocolHeader == null) + { + return false; + } + + if (_protocolHeader.length != pi._protocolHeader.length) + { + return false; + } + + for (int i = 0; i < _protocolHeader.length; i++) + { + if (_protocolHeader[i] != pi._protocolHeader[i]) + { + return false; + } + } + + return (_protocolClass == pi._protocolClass && + _protocolInstance == pi._protocolInstance && + _protocolMajor == pi._protocolMajor && + _protocolMinor == pi._protocolMinor); + } + + public static class Decoder //implements MessageDecoder + { + /** + * + * @param session the session + * @param in input buffer + * @return true if we have enough data to decode the PI frame fully, false if more + * data is required + */ + public boolean decodable(IoSession session, ByteBuffer in) + { + return (in.remaining() >= 8); + } + + public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) + { + ProtocolInitiation pi = new ProtocolInitiation(in); + out.write(pi); + } + } + + public ProtocolVersion checkVersion() throws AMQException + { + + if(_protocolHeader.length != 4) + { + throw new AMQProtocolHeaderException("Protocol header should have exactly four octets", null); + } + for(int i = 0; i < 4; i++) + { + if(_protocolHeader[i] != AMQP_HEADER[i]) + { + try + { + throw new AMQProtocolHeaderException("Protocol header is not correct: Got " + new String(_protocolHeader,"ISO-8859-1") + " should be: " + new String(AMQP_HEADER, "ISO-8859-1"), null); + } + catch (UnsupportedEncodingException e) + { + + } + } + } + if (_protocolClass != CURRENT_PROTOCOL_CLASS) + { + throw new AMQProtocolClassException("Protocol class " + CURRENT_PROTOCOL_CLASS + " was expected; received " + + _protocolClass, null); + } + if (_protocolInstance != TCP_PROTOCOL_INSTANCE) + { + throw new AMQProtocolInstanceException("Protocol instance " + TCP_PROTOCOL_INSTANCE + " was expected; received " + + _protocolInstance, null); + } + + ProtocolVersion pv = new ProtocolVersion(_protocolMajor, _protocolMinor); + + + if (!pv.isSupported()) + { + // TODO: add list of available versions in list to msg... + throw new AMQProtocolVersionException("Protocol version " + + _protocolMajor + "." + _protocolMinor + " not suppoerted by this version of the Qpid broker.", null); + } + return pv; + } + + public String toString() + { + StringBuffer buffer = new StringBuffer(new String(_protocolHeader)); + buffer.append(Integer.toHexString(_protocolClass)); + buffer.append(Integer.toHexString(_protocolInstance)); + buffer.append(Integer.toHexString(_protocolMajor)); + buffer.append(Integer.toHexString(_protocolMinor)); + return buffer.toString(); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.java new file mode 100644 index 0000000000..bd763599b0 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/SmallCompositeAMQDataBlock.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.framing; + +import org.apache.mina.common.ByteBuffer; + +public class SmallCompositeAMQDataBlock extends AMQDataBlock implements EncodableAMQDataBlock +{ + private AMQDataBlock _firstFrame; + + private AMQDataBlock _block; + + public SmallCompositeAMQDataBlock(AMQDataBlock block) + { + _block = block; + } + + /** + * The encoded block will be logically first before the AMQDataBlocks which are encoded + * into the buffer afterwards. + * @param encodedBlock already-encoded data + * @param block a block to be encoded. + */ + public SmallCompositeAMQDataBlock(AMQDataBlock encodedBlock, AMQDataBlock block) + { + this(block); + _firstFrame = encodedBlock; + } + + public AMQDataBlock getBlock() + { + return _block; + } + + public AMQDataBlock getFirstFrame() + { + return _firstFrame; + } + + public long getSize() + { + long frameSize = _block.getSize(); + + if (_firstFrame != null) + { + + frameSize += _firstFrame.getSize(); + } + return frameSize; + } + + public void writePayload(ByteBuffer buffer) + { + if (_firstFrame != null) + { + _firstFrame.writePayload(buffer); + } + _block.writePayload(buffer); + + } + + public String toString() + { + if (_block == null) + { + return "No blocks contained in composite frame"; + } + else + { + StringBuilder buf = new StringBuilder(this.getClass().getName()); + buf.append("{encodedBlock=").append(_firstFrame); + + buf.append(" _block=[").append(_block.toString()).append("]"); + + buf.append("}"); + return buf.toString(); + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.java new file mode 100644 index 0000000000..76c154581d --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/VersionSpecificRegistry.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.framing; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VersionSpecificRegistry +{ + private static final Logger _log = LoggerFactory.getLogger(VersionSpecificRegistry.class); + + private final byte _protocolMajorVersion; + private final byte _protocolMinorVersion; + + private static final int DEFAULT_MAX_CLASS_ID = 200; + private static final int DEFAULT_MAX_METHOD_ID = 50; + + private AMQMethodBodyInstanceFactory[][] _registry = new AMQMethodBodyInstanceFactory[DEFAULT_MAX_CLASS_ID][]; + + private ProtocolVersionMethodConverter _protocolVersionConverter; + + public VersionSpecificRegistry(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + + _protocolVersionConverter = loadProtocolVersionConverters(major, minor); + } + + private static ProtocolVersionMethodConverter loadProtocolVersionConverters(byte protocolMajorVersion, + byte protocolMinorVersion) + { + try + { + Class versionMethodConverterClass = + (Class) Class.forName("org.apache.qpid.framing.MethodConverter_" + + protocolMajorVersion + "_" + protocolMinorVersion); + + return versionMethodConverterClass.newInstance(); + + } + catch (ClassNotFoundException e) + { + _log.warn("Could not find protocol conversion classes for " + protocolMajorVersion + "-" + protocolMinorVersion); + if (protocolMinorVersion != 0) + { + protocolMinorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else if (protocolMajorVersion != 0) + { + protocolMajorVersion--; + + return loadProtocolVersionConverters(protocolMajorVersion, protocolMinorVersion); + } + else + { + return null; + } + + } + catch (IllegalAccessException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + catch (InstantiationException e) + { + throw new IllegalStateException("Unable to load protocol version converter: ", e); + } + } + + public byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } + + public AMQMethodBodyInstanceFactory getMethodBody(final short classID, final short methodID) + { + try + { + return _registry[classID][methodID]; + } + catch (IndexOutOfBoundsException e) + { + return null; + } + catch (NullPointerException e) + { + return null; + } + } + + public void registerMethod(final short classID, final short methodID, final AMQMethodBodyInstanceFactory instanceFactory) + { + if (_registry.length <= classID) + { + AMQMethodBodyInstanceFactory[][] oldRegistry = _registry; + _registry = new AMQMethodBodyInstanceFactory[classID + 1][]; + System.arraycopy(oldRegistry, 0, _registry, 0, oldRegistry.length); + } + + if (_registry[classID] == null) + { + _registry[classID] = + new AMQMethodBodyInstanceFactory[(methodID > DEFAULT_MAX_METHOD_ID) ? (methodID + 1) + : (DEFAULT_MAX_METHOD_ID + 1)]; + } + else if (_registry[classID].length <= methodID) + { + AMQMethodBodyInstanceFactory[] oldMethods = _registry[classID]; + _registry[classID] = new AMQMethodBodyInstanceFactory[methodID + 1]; + System.arraycopy(oldMethods, 0, _registry[classID], 0, oldMethods.length); + } + + _registry[classID][methodID] = instanceFactory; + + } + + public AMQMethodBody get(short classID, short methodID, ByteBuffer in, long size) throws AMQFrameDecodingException + { + AMQMethodBodyInstanceFactory bodyFactory; + try + { + bodyFactory = _registry[classID][methodID]; + } + catch (NullPointerException e) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + } + catch (IndexOutOfBoundsException e) + { + if (classID >= _registry.length) + { + throw new AMQFrameDecodingException(null, "Class " + classID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + else + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", e); + + } + } + + if (bodyFactory == null) + { + throw new AMQFrameDecodingException(null, "Method " + methodID + " unknown in AMQP version " + + _protocolMajorVersion + "-" + _protocolMinorVersion + " (while trying to decode class " + classID + + " method " + methodID + ".", null); + } + + return bodyFactory.newInstance( in, size); + + } + + public ProtocolVersionMethodConverter getProtocolVersionMethodConverter() + { + return _protocolVersionConverter; + } + + public void configure() + { + _protocolVersionConverter.configure(); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.java new file mode 100644 index 0000000000..1d7c05e9cc --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/AbstractMethodConverter.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.framing.abstraction; + +public abstract class AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private final byte _protocolMajorVersion; + + + private final byte _protocolMinorVersion; + + public AbstractMethodConverter(byte major, byte minor) + { + _protocolMajorVersion = major; + _protocolMinorVersion = minor; + } + + + public final byte getProtocolMajorVersion() + { + return _protocolMajorVersion; + } + + public final byte getProtocolMinorVersion() + { + return _protocolMinorVersion; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.java new file mode 100644 index 0000000000..0695349f76 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ContentChunk.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.framing.abstraction; + +import org.apache.mina.common.ByteBuffer; + +public interface ContentChunk +{ + int getSize(); + ByteBuffer getData(); + + void reduceToFit(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.java new file mode 100644 index 0000000000..a96bdcc171 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfo.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.framing.abstraction; + +import org.apache.qpid.framing.AMQShortString; + +public interface MessagePublishInfo +{ + + public AMQShortString getExchange(); + + public void setExchange(AMQShortString exchange); + + public boolean isImmediate(); + + public boolean isMandatory(); + + public AMQShortString getRoutingKey(); + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.java new file mode 100644 index 0000000000..01d1a8a17b --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/MessagePublishInfoConverter.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.framing.abstraction; + +import org.apache.qpid.framing.AMQMethodBody; + + +public interface MessagePublishInfoConverter +{ + public MessagePublishInfo convertToInfo(AMQMethodBody body); + public AMQMethodBody convertToBody(MessagePublishInfo info); + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.java new file mode 100644 index 0000000000..0a1cedc4e6 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/abstraction/ProtocolVersionMethodConverter.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.framing.abstraction; + +import org.apache.qpid.framing.AMQBody; + +public interface ProtocolVersionMethodConverter extends MessagePublishInfoConverter +{ + AMQBody convertToBody(ContentChunk contentBody); + ContentChunk convertToContentChunk(AMQBody body); + + void configure(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.java new file mode 100644 index 0000000000..948f5baaf6 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/AMQMethodBody_0_9.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.framing.amqp_0_9; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_0_9 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 0; + } + + public byte getMinor() + { + return 9; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 :response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java new file mode 100644 index 0000000000..fd5195eb6b --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_0_9/MethodConverter_0_9.java @@ -0,0 +1,172 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.framing.amqp_0_9; + +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.*; +import org.apache.qpid.framing.amqp_0_9.*; +import org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl; + +public class MethodConverter_0_9 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_0_9() + { + super((byte)0,(byte)9); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + if(contentChunk instanceof ContentChunk_0_9) + { + return ((ContentChunk_0_9)contentChunk).toBody(); + } + else + { + return new ContentBody(contentChunk.getData()); + } + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk_0_9(contentBodyChunk); + + } + + public void configure() + { + + _basicPublishClassId = org.apache.qpid.framing.amqp_0_9.BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MethodConverter_0_9.MessagePublishInfoImpl(exchange, + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } + + private static class ContentChunk_0_9 implements ContentChunk + { + private final ContentBody _contentBodyChunk; + + public ContentChunk_0_9(final ContentBody contentBodyChunk) + { + _contentBodyChunk = contentBodyChunk; + } + + public int getSize() + { + return _contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return _contentBodyChunk.payload; + } + + public void reduceToFit() + { + _contentBodyChunk.reduceBufferToFit(); + } + + public AMQBody toBody() + { + return _contentBodyChunk; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.java new file mode 100644 index 0000000000..e9b4447140 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/AMQMethodBody_8_0.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.framing.amqp_8_0; + +import org.apache.qpid.framing.EncodingUtils; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.framing.AMQFrameDecodingException; +import org.apache.qpid.framing.Content; + +import org.apache.mina.common.ByteBuffer; + +public abstract class AMQMethodBody_8_0 extends org.apache.qpid.framing.AMQMethodBodyImpl +{ + + public byte getMajor() + { + return 8; + } + + public byte getMinor() + { + return 0; + } + + public int getSize() + { + return 2 + 2 + getBodySize(); + } + + public void writePayload(ByteBuffer buffer) + { + EncodingUtils.writeUnsignedShort(buffer, getClazz()); + EncodingUtils.writeUnsignedShort(buffer, getMethod()); + writeMethodPayload(buffer); + } + + + protected byte readByte(ByteBuffer buffer) + { + return buffer.get(); + } + + protected AMQShortString readAMQShortString(ByteBuffer buffer) + { + return EncodingUtils.readAMQShortString(buffer); + } + + protected int getSizeOf(AMQShortString string) + { + return EncodingUtils.encodedShortStringLength(string); + } + + protected void writeByte(ByteBuffer buffer, byte b) + { + buffer.put(b); + } + + protected void writeAMQShortString(ByteBuffer buffer, AMQShortString string) + { + EncodingUtils.writeShortStringBytes(buffer, string); + } + + protected int readInt(ByteBuffer buffer) + { + return buffer.getInt(); + } + + protected void writeInt(ByteBuffer buffer, int i) + { + buffer.putInt(i); + } + + protected FieldTable readFieldTable(ByteBuffer buffer) throws AMQFrameDecodingException + { + return EncodingUtils.readFieldTable(buffer); + } + + protected int getSizeOf(FieldTable table) + { + return EncodingUtils.encodedFieldTableLength(table); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeFieldTable(ByteBuffer buffer, FieldTable table) + { + EncodingUtils.writeFieldTableBytes(buffer, table); + } + + protected long readLong(ByteBuffer buffer) + { + return buffer.getLong(); + } + + protected void writeLong(ByteBuffer buffer, long l) + { + buffer.putLong(l); + } + + protected int getSizeOf(byte[] response) + { + return (response == null) ? 4 : response.length + 4; + } + + protected void writeBytes(ByteBuffer buffer, byte[] data) + { + EncodingUtils.writeBytes(buffer,data); + } + + protected byte[] readBytes(ByteBuffer buffer) + { + return EncodingUtils.readBytes(buffer); + } + + protected short readShort(ByteBuffer buffer) + { + return EncodingUtils.readShort(buffer); + } + + protected void writeShort(ByteBuffer buffer, short s) + { + EncodingUtils.writeShort(buffer, s); + } + + protected Content readContent(ByteBuffer buffer) + { + return null; //To change body of created methods use File | Settings | File Templates. + } + + protected int getSizeOf(Content body) + { + return 0; //To change body of created methods use File | Settings | File Templates. + } + + protected void writeContent(ByteBuffer buffer, Content body) + { + //To change body of created methods use File | Settings | File Templates. + } + + protected byte readBitfield(ByteBuffer buffer) + { + return readByte(buffer); //To change body of created methods use File | Settings | File Templates. + } + + protected int readUnsignedShort(ByteBuffer buffer) + { + return buffer.getUnsignedShort(); //To change body of created methods use File | Settings | File Templates. + } + + protected void writeBitfield(ByteBuffer buffer, byte bitfield0) + { + buffer.put(bitfield0); + } + + protected void writeUnsignedShort(ByteBuffer buffer, int s) + { + EncodingUtils.writeUnsignedShort(buffer, s); + } + + protected long readUnsignedInteger(ByteBuffer buffer) + { + return buffer.getUnsignedInt(); + } + protected void writeUnsignedInteger(ByteBuffer buffer, long i) + { + EncodingUtils.writeUnsignedInteger(buffer, i); + } + + + protected short readUnsignedByte(ByteBuffer buffer) + { + return buffer.getUnsigned(); + } + + protected void writeUnsignedByte(ByteBuffer buffer, short unsignedByte) + { + EncodingUtils.writeUnsignedByte(buffer, unsignedByte); + } + + protected long readTimestamp(ByteBuffer buffer) + { + return EncodingUtils.readTimestamp(buffer); + } + + protected void writeTimestamp(ByteBuffer buffer, long t) + { + EncodingUtils.writeTimestamp(buffer, t); + } + + +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.java new file mode 100644 index 0000000000..299c655698 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/framing/amqp_8_0/MethodConverter_8_0.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.framing.amqp_8_0; + +import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter; +import org.apache.qpid.framing.abstraction.ContentChunk; +import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.framing.abstraction.AbstractMethodConverter; +import org.apache.qpid.framing.amqp_8_0.BasicPublishBodyImpl; +import org.apache.qpid.framing.*; + +import org.apache.mina.common.ByteBuffer; + +public class MethodConverter_8_0 extends AbstractMethodConverter implements ProtocolVersionMethodConverter +{ + private int _basicPublishClassId; + private int _basicPublishMethodId; + + public MethodConverter_8_0() + { + super((byte)8,(byte)0); + + + } + + public AMQBody convertToBody(ContentChunk contentChunk) + { + return new ContentBody(contentChunk.getData()); + } + + public ContentChunk convertToContentChunk(AMQBody body) + { + final ContentBody contentBodyChunk = (ContentBody) body; + + return new ContentChunk() + { + + public int getSize() + { + return contentBodyChunk.getSize(); + } + + public ByteBuffer getData() + { + return contentBodyChunk.payload; + } + + public void reduceToFit() + { + contentBodyChunk.reduceBufferToFit(); + } + }; + + } + + public void configure() + { + + _basicPublishClassId = BasicPublishBodyImpl.CLASS_ID; + _basicPublishMethodId = BasicPublishBodyImpl.METHOD_ID; + + } + + public MessagePublishInfo convertToInfo(AMQMethodBody methodBody) + { + final BasicPublishBody publishBody = ((BasicPublishBody) methodBody); + + final AMQShortString exchange = publishBody.getExchange(); + final AMQShortString routingKey = publishBody.getRoutingKey(); + + return new MessagePublishInfoImpl(exchange == null ? null : exchange.intern(), + publishBody.getImmediate(), + publishBody.getMandatory(), + routingKey == null ? null : routingKey.intern()); + + } + + public AMQMethodBody convertToBody(MessagePublishInfo info) + { + + return new BasicPublishBodyImpl(0, + info.getExchange(), + info.getRoutingKey(), + info.isMandatory(), + info.isImmediate()) ; + + } + + private static class MessagePublishInfoImpl implements MessagePublishInfo + { + private AMQShortString _exchange; + private final boolean _immediate; + private final boolean _mandatory; + private final AMQShortString _routingKey; + + public MessagePublishInfoImpl(final AMQShortString exchange, + final boolean immediate, + final boolean mandatory, + final AMQShortString routingKey) + { + _exchange = exchange; + _immediate = immediate; + _mandatory = mandatory; + _routingKey = routingKey; + } + + public AMQShortString getExchange() + { + return _exchange; + } + + public void setExchange(AMQShortString exchange) + { + _exchange = exchange; + } + + public boolean isImmediate() + { + return _immediate; + } + + public boolean isMandatory() + { + return _mandatory; + } + + public AMQShortString getRoutingKey() + { + return _routingKey; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/Event.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/Event.java new file mode 100644 index 0000000000..5996cbf89c --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/Event.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.pool; + +import org.apache.mina.common.IoFilter; +import org.apache.mina.common.IoSession; + +/** + * An Event is a continuation, which is used to break a Mina filter chain and save the current point in the chain + * for later processing. It is an abstract class, with different implementations for continuations of different kinds + * of Mina events. + * + *

These continuations are typically batched by {@link Job} for processing by a worker thread pool. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Process a continuation in the context of a Mina session. + *
+ * + * @todo Pull up _nextFilter and getNextFilter into Event, as all events use it. Inner classes need to be non-static + * to use instance variables in the parent. Consequently they need to be non-inner to be instantiable outside of + * the context of the outer Event class. The inner class construction used here is preventing common code re-use + * (though not by a huge amount), but makes for an inelegent way of handling inheritance and doesn't seem like + * a justifiable use of inner classes. Move the inner classes out into their own files. + * + * @todo Could make Event implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as + * a continuation. Job is also a continuation, as is the job completion handler. Or, as Event is totally abstract, + * it is really an interface, so could just drop it and use the continuation interface instead. + */ +public abstract class Event +{ + /** + * Creates a continuation. + */ + public Event() + { } + + /** + * Processes the continuation in the context of a Mina session. + * + * @param session The Mina session. + */ + public abstract void process(IoSession session); + + /** + * A continuation ({@link Event}) that takes a Mina messageReceived event, and passes it to a NextFilter. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Pass a Mina messageReceived event to a NextFilter. {@link IoFilter.NextFilter}, {@link IoSession} + *
+ */ + public static final class ReceivedEvent extends Event + { + private final Object _data; + + private final IoFilter.NextFilter _nextFilter; + + public ReceivedEvent(final IoFilter.NextFilter nextFilter, final Object data) + { + super(); + _nextFilter = nextFilter; + _data = data; + } + + public void process(IoSession session) + { + _nextFilter.messageReceived(session, _data); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } + + /** + * A continuation ({@link Event}) that takes a Mina filterWrite event, and passes it to a NextFilter. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Pass a Mina filterWrite event to a NextFilter. + * {@link IoFilter.NextFilter}, {@link IoFilter.WriteRequest}, {@link IoSession} + *
+ */ + public static final class WriteEvent extends Event + { + private final IoFilter.WriteRequest _data; + private final IoFilter.NextFilter _nextFilter; + + public WriteEvent(final IoFilter.NextFilter nextFilter, final IoFilter.WriteRequest data) + { + super(); + _nextFilter = nextFilter; + _data = data; + } + + public void process(IoSession session) + { + _nextFilter.filterWrite(session, _data); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } + + /** + * A continuation ({@link Event}) that takes a Mina sessionClosed event, and passes it to a NextFilter. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Pass a Mina sessionClosed event to a NextFilter. {@link IoFilter.NextFilter}, {@link IoSession} + *
+ */ + public static final class CloseEvent extends Event + { + private final IoFilter.NextFilter _nextFilter; + + public CloseEvent(final IoFilter.NextFilter nextFilter) + { + super(); + _nextFilter = nextFilter; + } + + public void process(IoSession session) + { + _nextFilter.sessionClosed(session); + } + + public IoFilter.NextFilter getNextFilter() + { + return _nextFilter; + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/Job.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/Job.java new file mode 100644 index 0000000000..00da005515 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/Job.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.pool; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.mina.common.IoSession; + +/** + * A Job is a continuation that batches together other continuations, specifically {@link Event}s, into one continuation. + * The {@link Event}s themselves provide methods to process themselves, so processing a job simply consists of sequentially + * processing all of its aggregated events. + * + * The constructor accepts a maximum number of events for the job, and only runs up to that maximum number when + * processing the job, but the add method does not enforce this maximum. In other words, not all the enqueued events + * may be processed in each run of the job, several runs may be required to clear the queue. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Aggregate many coninuations together into a single continuation. + *
Sequentially process aggregated continuations. {@link Event} + *
Provide running and completion status of the aggregate continuation. + *
Execute a terminal continuation upon job completion. {@link JobCompletionHandler} + *
+ * + * @todo Could make Job implement Runnable, FutureTask, or a custom Continuation interface, to clarify its status as a + * continuation. Job is a continuation that aggregates other continuations and as such is a usefull re-usable + * piece of code. There may be other palces than the mina filter chain where continuation batching is used within + * qpid, so abstracting this out could provide a usefull building block. This also opens the way to different + * kinds of job with a common interface, e.g. parallel or sequential jobs etc. + * + * @todo For better re-usability could make the completion handler optional. Only run it when one is set. + */ +public class Job implements ReadWriteRunnable +{ + /** The maximum number of events to process per run of the job. More events than this may be queued in the job. */ + private final int _maxEvents; + + /** The Mina session. */ + private final IoSession _session; + + /** Holds the queue of events that make up the job. */ + private final java.util.Queue _eventQueue = new ConcurrentLinkedQueue(); + + /** Holds a status flag, that indicates when the job is actively running. */ + private final AtomicBoolean _active = new AtomicBoolean(); + + /** Holds the completion continuation, called upon completion of a run of the job. */ + private final JobCompletionHandler _completionHandler; + + private final boolean _readJob; + + /** + * Creates a new job that aggregates many continuations together. + * + * @param session The Mina session. + * @param completionHandler The per job run, terminal continuation. + * @param maxEvents The maximum number of aggregated continuations to process per run of the job. + * @param readJob + */ + Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob) + { + _session = session; + _completionHandler = completionHandler; + _maxEvents = maxEvents; + _readJob = readJob; + } + + /** + * Enqueus a continuation for sequential processing by this job. + * + * @param evt The continuation to enqueue. + */ + void add(Event evt) + { + _eventQueue.add(evt); + } + + /** + * Sequentially processes, up to the maximum number per job, the aggregated continuations in enqueued in this job. + */ + boolean processAll() + { + // limit the number of events processed in one run + int i = _maxEvents; + while( --i != 0 ) + { + Event e = _eventQueue.poll(); + if (e == null) + { + return true; + } + else + { + e.process(_session); + } + } + return false; + } + + /** + * Tests if there are no more enqueued continuations to process. + * + * @return true if there are no enqueued continuations in this job, false otherwise. + */ + public boolean isComplete() + { + return _eventQueue.peek() == null; + } + + /** + * Marks this job as active if it is inactive. This method is thread safe. + * + * @return true if this job was inactive and has now been marked as active, false otherwise. + */ + public boolean activate() + { + return _active.compareAndSet(false, true); + } + + /** + * Marks this job as inactive. This method is thread safe. + */ + public void deactivate() + { + _active.set(false); + } + + /** + * Processes a batch of aggregated continuations, marks this job as inactive and call the terminal continuation. + */ + public void run() + { + if(processAll()) + { + deactivate(); + _completionHandler.completed(_session, this); + } + else + { + _completionHandler.notCompleted(_session, this); + } + } + + public boolean isReadJob() + { + return _readJob; + } + + public boolean isRead() + { + return _readJob; + } + + public boolean isWrite() + { + return !_readJob; + } + + + /** + * Another interface for a continuation. + * + * @todo Get rid of this interface as there are other interfaces that could be used instead, such as FutureTask, + * Runnable or a custom Continuation interface. + */ + static interface JobCompletionHandler + { + public void completed(IoSession session, Job job); + + public void notCompleted(final IoSession session, final Job job); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java new file mode 100644 index 0000000000..a080cc7e04 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java @@ -0,0 +1,491 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.pool; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.apache.mina.common.IdleStatus; +import org.apache.mina.common.IoFilterAdapter; +import org.apache.mina.common.IoSession; +import org.apache.qpid.pool.Event.CloseEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ExecutorService; + +/** + * PoolingFilter, is a no-op pass through filter that hands all events down the Mina filter chain by default. As it + * adds no behaviour by default to the filter chain, it is abstract. + * + *

PoolingFilter provides a capability, available to sub-classes, to handle events in the chain asynchronously, by + * adding them to a job. If a job is not active, adding an event to it activates it. If it is active, the event is + * added to the job, which will run to completion and eventually process the event. The queue on the job itself acts as + * a buffer between stages of the pipeline. + * + *

There are two convenience methods, {@link #createAynschReadPoolingFilter} and + * {@link #createAynschWritePoolingFilter}, for obtaining pooling filters that handle 'messageReceived' and + * 'filterWrite' events, making it possible to process these event streams seperately. + * + *

Pooling filters have a name, in order to distinguish different filter types. They set up a {@link Job} on the + * Mina session they are working with, and store it in the session against their identifying name. This allows different + * filters with different names to be set up on the same filter chain, on the same Mina session, that batch their + * workloads in different jobs. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Implement default, pass through filter. + *
Create pooling filters and a specific thread pool. {@link ReferenceCountingExecutorService} + *
Provide the ability to batch Mina events for asynchronous processing. {@link Job}, {@link Event} + *
Provide a terminal continuation to keep jobs running till empty. + * {@link Job}, {@link Job.JobCompletionHandler} + *
+ * + * @todo The static helper methods are pointless. Could just call new. + */ +public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler +{ + /** Used for debugging purposes. */ + private static final Logger _logger = LoggerFactory.getLogger(PoolingFilter.class); + + /** Holds the managed reference to obtain the executor for the batched jobs. */ + private final ReferenceCountingExecutorService _poolReference; + + /** Used to hold a name for identifying differeny pooling filter types. */ + private final String _name; + + /** Defines the maximum number of events that will be batched into a single job. */ + static final int MAX_JOB_EVENTS = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); + + private final int _maxEvents; + + private final boolean _readFilter; + + /** + * Creates a named pooling filter, on the specified shared thread pool. + * + * @param refCountingPool The thread pool reference. + * @param name The identifying name of the filter type. + */ + public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents, boolean readFilter) + { + _poolReference = refCountingPool; + _name = name; + _maxEvents = maxEvents; + _readFilter = readFilter; + } + + /** + * Helper method to get an instance of a pooling filter that handles read events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + * + * @return A pooling filter for asynchronous read events. + */ + public static PoolingFilter createAynschReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + return new AsynchReadPoolingFilter(refCountingPool, name); + } + + /** + * Helper method to get an instance of a pooling filter that handles write events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + * + * @return A pooling filter for asynchronous write events. + */ + public static PoolingFilter createAynschWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + return new AsynchWritePoolingFilter(refCountingPool, name); + } + + /** + * Called by Mina to initialize this filter. Takes a reference to the thread pool. + */ + public void init() + { + _logger.debug("Init called on PoolingFilter " + toString()); + + // Called when the filter is initialised in the chain. If the reference count is + // zero this acquire will initialise the pool. + _poolReference.acquireExecutorService(); + } + + /** + * Called by Mina to clean up this filter. Releases the reference to the thread pool. + */ + public void destroy() + { + _logger.debug("Destroy called on PoolingFilter " + toString()); + + // When the reference count gets to zero we release the executor service. + _poolReference.releaseExecutorService(); + } + + /** + * Adds an {@link Event} to a {@link Job}, triggering the execution of the job if it is not already running. + * + * @param job The job. + * @param event The event to hand off asynchronously. + */ + void fireAsynchEvent(Job job, Event event) + { + + job.add(event); + + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + // rather than perform additional checks on pool to check that it hasn't shutdown. + // catch the RejectedExecutionException that will result from executing on a shutdown pool + if (job.activate()) + { + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + } + + } + + /** + * Creates a Job on the Mina session, identified by this filters name, in which this filter places asynchronously + * handled events. + * + * @param session The Mina session. + */ + public void createNewJobForSession(IoSession session) + { + Job job = new Job(session, this, MAX_JOB_EVENTS,_readFilter); + session.setAttribute(_name, job); + } + + /** + * Retrieves this filters Job, by this filters name, from the Mina session. + * + * @param session The Mina session. + * + * @return The Job for this filter to place asynchronous events into. + */ + public Job getJobForSession(IoSession session) + { + return (Job) session.getAttribute(_name); + } + + /** + * Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing + * of a batch of events this is called. This method simply re-activates the job, if it has more events to process. + * + * @param session The Mina session to work in. + * @param job The job that completed. + */ + public void completed(IoSession session, Job job) + { + + + if (!job.isComplete()) + { + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + + // ritchiem : 2006-12-13 Do we need to perform the additional checks here? + // Can the pool be shutdown at this point? + if (job.activate()) + { + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + + } + } + } + + public void notCompleted(IoSession session, Job job) + { + final ExecutorService pool = _poolReference.getPool(); + + if(pool == null) + { + return; + } + + try + { + pool.execute(job); + } + catch(RejectedExecutionException e) + { + _logger.warn("Thread pool shutdown while tasks still outstanding"); + } + + } + + + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionOpened(final NextFilter nextFilter, final IoSession session) throws Exception + { + nextFilter.sessionOpened(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) throws Exception + { + nextFilter.sessionClosed(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param status The session idle status. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionIdle(final NextFilter nextFilter, final IoSession session, final IdleStatus status) throws Exception + { + nextFilter.sessionIdle(session, status); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param cause The underlying exception. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void exceptionCaught(final NextFilter nextFilter, final IoSession session, final Throwable cause) throws Exception + { + nextFilter.exceptionCaught(session, cause); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message received. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void messageReceived(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception + { + nextFilter.messageReceived(session, message); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message sent. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void messageSent(final NextFilter nextFilter, final IoSession session, final Object message) throws Exception + { + nextFilter.messageSent(session, message); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param writeRequest The write request event. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) + throws Exception + { + nextFilter.filterWrite(session, writeRequest); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void filterClose(NextFilter nextFilter, IoSession session) throws Exception + { + nextFilter.filterClose(session); + } + + /** + * No-op pass through filter to the next filter in the chain. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * + * @throws Exception This method does not throw any exceptions, but has Exception in its signature to allow + * overriding sub-classes the ability to. + */ + public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception + { + nextFilter.sessionCreated(session); + } + + /** + * Prints the filter types identifying name to a string, mainly for debugging purposes. + * + * @return The filter types identifying name. + */ + public String toString() + { + return _name; + } + + /** + * AsynchReadPoolingFilter is a pooling filter that handles 'messageReceived' and 'sessionClosed' events + * asynchronously. + */ + public static class AsynchReadPoolingFilter extends PoolingFilter + { + /** + * Creates a pooling filter that handles read events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + */ + public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS),true); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param message The message received. + */ + public void messageReceived(NextFilter nextFilter, final IoSession session, Object message) + { + Job job = getJobForSession(session); + fireAsynchEvent(job, new Event.ReceivedEvent(nextFilter, message)); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) + { + Job job = getJobForSession(session); + fireAsynchEvent(job, new CloseEvent(nextFilter)); + } + } + + /** + * AsynchWritePoolingFilter is a pooling filter that handles 'filterWrite' and 'sessionClosed' events + * asynchronously. + */ + public static class AsynchWritePoolingFilter extends PoolingFilter + { + /** + * Creates a pooling filter that handles write events asynchronously. + * + * @param refCountingPool A managed reference to the thread pool. + * @param name The filter types identifying name. + */ + public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name) + { + super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS),false); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + * @param writeRequest The write request event. + */ + public void filterWrite(final NextFilter nextFilter, final IoSession session, final WriteRequest writeRequest) + { + Job job = getJobForSession(session); + fireAsynchEvent(job, new Event.WriteEvent(nextFilter, writeRequest)); + } + + /** + * Hands off this event for asynchronous execution. + * + * @param nextFilter The next filter in the chain. + * @param session The Mina session. + */ + public void sessionClosed(final NextFilter nextFilter, final IoSession session) + { + Job job = getJobForSession(session); + fireAsynchEvent(job, new CloseEvent(nextFilter)); + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java new file mode 100644 index 0000000000..8de0f93ce9 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java @@ -0,0 +1,432 @@ +package org.apache.qpid.pool; + +import java.util.AbstractQueue; +import java.util.Iterator; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.atomic.AtomicInteger; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +public class ReadWriteJobQueue extends AbstractQueue implements BlockingQueue +{ + + private final AtomicInteger _count = new AtomicInteger(0); + + private final ReentrantLock _takeLock = new ReentrantLock(); + + private final Condition _notEmpty = _takeLock.newCondition(); + + private final ReentrantLock _putLock = new ReentrantLock(); + + private final ConcurrentLinkedQueue _readJobQueue = new ConcurrentLinkedQueue(); + + private final ConcurrentLinkedQueue _writeJobQueue = new ConcurrentLinkedQueue(); + + + private class ReadWriteJobIterator implements Iterator + { + + private boolean _onReads; + private Iterator _iter = _writeJobQueue.iterator(); + + public boolean hasNext() + { + if(!_iter.hasNext()) + { + if(_onReads) + { + _iter = _readJobQueue.iterator(); + _onReads = true; + return _iter.hasNext(); + } + else + { + return false; + } + } + else + { + return true; + } + } + + public Runnable next() + { + if(_iter.hasNext()) + { + return _iter.next(); + } + else + { + return null; + } + } + + public void remove() + { + _takeLock.lock(); + try + { + _iter.remove(); + _count.decrementAndGet(); + } + finally + { + _takeLock.unlock(); + } + } + } + + public Iterator iterator() + { + return new ReadWriteJobIterator(); + } + + public int size() + { + return _count.get(); + } + + public boolean offer(final Runnable runnable) + { + final ReadWriteRunnable job = (ReadWriteRunnable) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + try + { + if(job.isRead()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + return true; + } + finally + { + putLock.unlock(); + } + } + + public void put(final Runnable runnable) throws InterruptedException + { + final ReadWriteRunnable job = (ReadWriteRunnable) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + + try + { + if(job.isRead()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + + } + finally + { + putLock.unlock(); + } + } + + + + public boolean offer(final Runnable runnable, final long timeout, final TimeUnit unit) throws InterruptedException + { + final ReadWriteRunnable job = (ReadWriteRunnable) runnable; + final ReentrantLock putLock = _putLock; + putLock.lock(); + + try + { + if(job.isRead()) + { + _readJobQueue.offer(job); + } + else + { + _writeJobQueue.offer(job); + } + if(_count.getAndIncrement() == 0) + { + _takeLock.lock(); + try + { + _notEmpty.signal(); + } + finally + { + _takeLock.unlock(); + } + } + + return true; + } + finally + { + putLock.unlock(); + } + + } + + public Runnable take() throws InterruptedException + { + final ReentrantLock takeLock = _takeLock; + takeLock.lockInterruptibly(); + try + { + try + { + while (_count.get() == 0) + { + _notEmpty.await(); + } + } + catch (InterruptedException ie) + { + _notEmpty.signal(); + throw ie; + } + + ReadWriteRunnable job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + int c = _count.getAndDecrement(); + if (c > 1) + { + _notEmpty.signal(); + } + return job; + } + finally + { + takeLock.unlock(); + } + + + } + + public Runnable poll(final long timeout, final TimeUnit unit) throws InterruptedException + { + final ReentrantLock takeLock = _takeLock; + final AtomicInteger count = _count; + long nanos = unit.toNanos(timeout); + takeLock.lockInterruptibly(); + ReadWriteRunnable job = null; + try + { + + for (;;) + { + if (count.get() > 0) + { + job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + int c = count.getAndDecrement(); + if (c > 1) + { + _notEmpty.signal(); + } + break; + } + if (nanos <= 0) + { + return null; + } + try + { + nanos = _notEmpty.awaitNanos(nanos); + } + catch (InterruptedException ie) + { + _notEmpty.signal(); + throw ie; + } + } + } + finally + { + takeLock.unlock(); + } + + return job; + } + + public int remainingCapacity() + { + return Integer.MAX_VALUE; + } + + public int drainTo(final Collection c) + { + int total = 0; + + _putLock.lock(); + _takeLock.lock(); + try + { + ReadWriteRunnable job; + while((job = _writeJobQueue.peek())!= null) + { + c.add(job); + _writeJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + while((job = _readJobQueue.peek())!= null) + { + c.add(job); + _readJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + } + finally + { + _takeLock.unlock(); + _putLock.unlock(); + } + return total; + } + + public int drainTo(final Collection c, final int maxElements) + { + int total = 0; + + _putLock.lock(); + _takeLock.lock(); + try + { + ReadWriteRunnable job; + while(total<=maxElements && (job = _writeJobQueue.peek())!= null) + { + c.add(job); + _writeJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + while(total<=maxElements && (job = _readJobQueue.peek())!= null) + { + c.add(job); + _readJobQueue.poll(); + _count.decrementAndGet(); + total++; + } + + } + finally + { + _takeLock.unlock(); + _putLock.unlock(); + } + return total; + + } + + public Runnable poll() + { + final ReentrantLock takeLock = _takeLock; + takeLock.lock(); + try + { + if(_count.get() > 0) + { + ReadWriteRunnable job = _writeJobQueue.poll(); + if(job == null) + { + job = _readJobQueue.poll(); + } + _count.decrementAndGet(); + return job; + } + else + { + return null; + } + } + finally + { + takeLock.unlock(); + } + + } + + public Runnable peek() + { + final ReentrantLock takeLock = _takeLock; + takeLock.lock(); + try + { + ReadWriteRunnable job = _writeJobQueue.peek(); + if(job == null) + { + job = _readJobQueue.peek(); + } + return job; + } + finally + { + takeLock.unlock(); + } + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java new file mode 100644 index 0000000000..ad04a923e1 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteRunnable.java @@ -0,0 +1,27 @@ +package org.apache.qpid.pool; + +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +public interface ReadWriteRunnable extends Runnable +{ + boolean isRead(); + boolean isWrite(); +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.java new file mode 100644 index 0000000000..8cea70e597 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReadWriteThreadModel.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.pool; + +import org.apache.mina.common.IoFilterChain; +import org.apache.mina.common.ThreadModel; +import org.apache.mina.filter.ReferenceCountingIoFilter; + +/** + * ReadWriteThreadModel is a Mina i/o filter chain factory, which creates a filter chain with seperate filters to + * handle read and write events. The seperate filters are {@link PoolingFilter}s, which have thread pools to handle + * these events. The effect of this is that reading and writing may happen concurrently. + * + *

Socket i/o will only happen with concurrent reads and writes if Mina has seperate selector threads for each. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Create a filter chain with seperate read and write thread pools for read/write Mina events. + * {@link PoolingFilter} + *
+ */ +public class ReadWriteThreadModel implements ThreadModel +{ + /** Holds the singleton instance of this factory. */ + private static final ReadWriteThreadModel _instance = new ReadWriteThreadModel(); + + /** Holds the thread pooling filter for reads. */ + private final PoolingFilter _asynchronousReadFilter; + + /** Holds the thread pooloing filter for writes. */ + private final PoolingFilter _asynchronousWriteFilter; + + /** + * Creates a new factory for concurrent i/o, thread pooling filter chain construction. This is private, so that + * only a singleton instance of the factory is ever created. + */ + private ReadWriteThreadModel() + { + final ReferenceCountingExecutorService executor = ReferenceCountingExecutorService.getInstance(); + _asynchronousReadFilter = PoolingFilter.createAynschReadPoolingFilter(executor, "AsynchronousReadFilter"); + _asynchronousWriteFilter = PoolingFilter.createAynschWritePoolingFilter(executor, "AsynchronousWriteFilter"); + } + + /** + * Gets the singleton instance of this filter chain factory. + * + * @return The singleton instance of this filter chain factory. + */ + public static ReadWriteThreadModel getInstance() + { + return _instance; + } + + /** + * Gets the read filter. + * + * @return The read filter. + */ + public PoolingFilter getAsynchronousReadFilter() + { + return _asynchronousReadFilter; + } + + /** + * Gets the write filter. + * + * @return The write filter. + */ + public PoolingFilter getAsynchronousWriteFilter() + { + return _asynchronousWriteFilter; + } + + /** + * Adds the concurrent read and write filters to a filter chain. + * + * @param chain The Mina filter chain to add to. + */ + public void buildFilterChain(IoFilterChain chain) + { + chain.addFirst("AsynchronousReadFilter", new ReferenceCountingIoFilter(_asynchronousReadFilter)); + chain.addLast("AsynchronousWriteFilter", new ReferenceCountingIoFilter(_asynchronousWriteFilter)); + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java new file mode 100644 index 0000000000..20a30b3ed3 --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java @@ -0,0 +1,172 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.pool; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * ReferenceCountingExecutorService wraps an ExecutorService in order to provide shared reference to it. It counts + * the references taken, instantiating the service on the first reference, and shutting it down when the last + * reference is released. + * + *

It is important to ensure that an executor service is correctly shut down as failing to do so prevents the JVM + * from terminating due to the existence of non-daemon threads. + * + *

null if none has been instantiated yet. + */ + public ExecutorService getPool() + { + return _pool; + } + + /** + * Return the ReferenceCount to this ExecutorService + * @return reference count + */ + public int getReferenceCount() + { + return _refCount; + } +} diff --git a/RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java b/RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.java new file mode 100644 index 0000000000..8dee790a9e --- /dev/null +++ b/RC5/java/common/src/main/java/org/apache/qpid/protocol/AMQConstant.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.protocol; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.qpid.framing.AMQShortString; + +/** + * Defines constants for AMQP codes and also acts as a factory for creating such constants from the raw codes. Each + * constant also defines a short human readable description of the constant. + * + * @todo Why would a constant be defined that is not in the map? Seems more natural that getConstant should raise an + * exception for an unknown constant. Or else provide an explanation of why this is so. Also, there is no way for + * callers to determine the unknown status of a code except by comparing its name to "unknown code", which would + * seem to render this scheme a little bit pointless? + * + * @todo Java has a nice enum construct for doing this sort of thing. Maybe this is done in the old style for Java 1.4 + * backward compatability? Now that is handled through retrotranslater it may be time to use enum. + * + *

+ *